QGIS API Documentation 3.38.0-Grenoble (exported)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
qgsadvanceddigitizingdockwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsadvanceddigitizingdockwidget.cpp - dock for CAD tools
3 ----------------------
4 begin : October 2014
5 copyright : (C) Denis Rouzaud
6 email : denis.rouzaud@gmail.com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include <QMenu>
17#include <QEvent>
18#include <QCoreApplication>
19
20#include <cmath>
21
26#include "qgscadutils.h"
27#include "qgsexpression.h"
28#include "qgsmapcanvas.h"
29#include "qgsmaptooledit.h"
31#include "qgsmessagebaritem.h"
32#include "qgsfocuswatcher.h"
33#include "qgssettings.h"
34#include "qgssnappingutils.h"
35#include "qgsproject.h"
36#include "qgsmapmouseevent.h"
37#include "qgsmeshlayer.h"
38#include "qgsunittypes.h"
40#include "qgssettingstree.h"
41
42#include <QActionGroup>
43
44
45const QgsSettingsEntryBool *QgsAdvancedDigitizingDockWidget::settingsCadSnappingPriorityPrioritizeFeature = new QgsSettingsEntryBool( QStringLiteral( "cad-snapping-prioritize-feature" ), QgsSettingsTree::sTreeDigitizing, false, tr( "Determines if snapping to features has priority over snapping to common angles." ) ) ;
46
47
49 : QgsDockWidget( parent )
50 , mMapCanvas( canvas )
51 , mSnapIndicator( std::make_unique< QgsSnapIndicator>( canvas ) )
52 , mCommonAngleConstraint( QgsSettings().value( QStringLiteral( "/Cad/CommonAngle" ), 0.0 ).toDouble() )
53{
54 setupUi( this );
55
56 mCadPaintItem = new QgsAdvancedDigitizingCanvasItem( canvas, this );
57
58 mAngleConstraint.reset( new CadConstraint( mAngleLineEdit, mLockAngleButton, mRelativeAngleButton, mRepeatingLockAngleButton ) );
59 mAngleConstraint->setCadConstraintType( Qgis::CadConstraintType::Angle );
60 mAngleConstraint->setMapCanvas( mMapCanvas );
61 mDistanceConstraint.reset( new CadConstraint( mDistanceLineEdit, mLockDistanceButton, nullptr, mRepeatingLockDistanceButton ) );
62 mDistanceConstraint->setCadConstraintType( Qgis::CadConstraintType::Distance );
63 mDistanceConstraint->setMapCanvas( mMapCanvas );
64 mXConstraint.reset( new CadConstraint( mXLineEdit, mLockXButton, mRelativeXButton, mRepeatingLockXButton ) );
65 mXConstraint->setCadConstraintType( Qgis::CadConstraintType::XCoordinate );
66 mXConstraint->setMapCanvas( mMapCanvas );
67 mYConstraint.reset( new CadConstraint( mYLineEdit, mLockYButton, mRelativeYButton, mRepeatingLockYButton ) );
68 mYConstraint->setCadConstraintType( Qgis::CadConstraintType::YCoordinate );
69 mYConstraint->setMapCanvas( mMapCanvas );
70 mZConstraint.reset( new CadConstraint( mZLineEdit, mLockZButton, mRelativeZButton, mRepeatingLockZButton ) );
71 mZConstraint->setCadConstraintType( Qgis::CadConstraintType::ZValue );
72 mZConstraint->setMapCanvas( mMapCanvas );
73 mMConstraint.reset( new CadConstraint( mMLineEdit, mLockMButton, mRelativeMButton, mRepeatingLockMButton ) );
74 mMConstraint->setCadConstraintType( Qgis::CadConstraintType::MValue );
75 mMConstraint->setMapCanvas( mMapCanvas );
76
77 mLineExtensionConstraint.reset( new CadConstraint( new QLineEdit(), new QToolButton() ) );
78 mXyVertexConstraint.reset( new CadConstraint( new QLineEdit(), new QToolButton() ) );
79 mXyVertexConstraint->setMapCanvas( mMapCanvas );
80
81 mBetweenLineConstraint = Qgis::BetweenLineConstraint::NoConstraint;
82
83 mMapCanvas->installEventFilter( this );
84 mAngleLineEdit->installEventFilter( this );
85 mDistanceLineEdit->installEventFilter( this );
86 mXLineEdit->installEventFilter( this );
87 mYLineEdit->installEventFilter( this );
88 mZLineEdit->installEventFilter( this );
89 mMLineEdit->installEventFilter( this );
90
91 // Connect the UI to the event filter to update constraints
92 connect( mEnableAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::activateCad );
93 connect( mConstructionModeAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::setConstructionMode );
94 connect( mParallelAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
95 connect( mPerpendicularAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
96 connect( mLockAngleButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
97 connect( mLockDistanceButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
98 connect( mLockXButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
99 connect( mLockYButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
100 connect( mLockZButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
101 connect( mLockMButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
102 connect( mRelativeAngleButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
103 connect( mRelativeXButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
104 connect( mRelativeYButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
105 connect( mRelativeZButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
106 connect( mRelativeMButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
107 connect( mRepeatingLockDistanceButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
108 connect( mRepeatingLockAngleButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
109 connect( mRepeatingLockXButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
110 connect( mRepeatingLockYButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
111 connect( mRepeatingLockZButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
112 connect( mRepeatingLockMButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
113 connect( mAngleLineEdit, &QLineEdit::returnPressed, this, [ = ]() { lockConstraint(); } );
114 connect( mDistanceLineEdit, &QLineEdit::returnPressed, this, [ = ]() { lockConstraint(); } );
115 connect( mXLineEdit, &QLineEdit::returnPressed, this, [ = ]() { lockConstraint(); } );
116 connect( mYLineEdit, &QLineEdit::returnPressed, this, [ = ]() { lockConstraint(); } );
117 connect( mZLineEdit, &QLineEdit::returnPressed, this, [ = ]() { lockConstraint(); } );
118 connect( mMLineEdit, &QLineEdit::returnPressed, this, [ = ]() { lockConstraint(); } );
119 connect( mAngleLineEdit, &QLineEdit::textEdited, this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
120 connect( mDistanceLineEdit, &QLineEdit::textEdited, this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
121 connect( mXLineEdit, &QLineEdit::textEdited, this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
122 connect( mYLineEdit, &QLineEdit::textEdited, this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
123 connect( mZLineEdit, &QLineEdit::textEdited, this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
124 connect( mMLineEdit, &QLineEdit::textEdited, this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
125 //also watch for focus out events on these widgets
126 QgsFocusWatcher *angleWatcher = new QgsFocusWatcher( mAngleLineEdit );
127 connect( angleWatcher, &QgsFocusWatcher::focusOut, this, &QgsAdvancedDigitizingDockWidget::constraintFocusOut );
128 connect( angleWatcher, &QgsFocusWatcher::focusIn, this, [ = ]()
129 {
130 const QString cleanedInputValue { QgsAdvancedDigitizingDockWidget::CadConstraint::removeSuffix( mAngleLineEdit->text(), Qgis::CadConstraintType::Angle ) };
131 whileBlocking( mAngleLineEdit )->setText( cleanedInputValue );
132 } );
133 QgsFocusWatcher *distanceWatcher = new QgsFocusWatcher( mDistanceLineEdit );
134 connect( distanceWatcher, &QgsFocusWatcher::focusOut, this, &QgsAdvancedDigitizingDockWidget::constraintFocusOut );
135 connect( distanceWatcher, &QgsFocusWatcher::focusIn, this, [ = ]()
136 {
137 const QString cleanedInputValue { QgsAdvancedDigitizingDockWidget::CadConstraint::removeSuffix( mDistanceLineEdit->text(), Qgis::CadConstraintType::Distance ) };
138 whileBlocking( mDistanceLineEdit )->setText( cleanedInputValue );
139 } );
140 QgsFocusWatcher *xWatcher = new QgsFocusWatcher( mXLineEdit );
141 connect( xWatcher, &QgsFocusWatcher::focusOut, this, &QgsAdvancedDigitizingDockWidget::constraintFocusOut );
142 QgsFocusWatcher *yWatcher = new QgsFocusWatcher( mYLineEdit );
143 connect( yWatcher, &QgsFocusWatcher::focusOut, this, &QgsAdvancedDigitizingDockWidget::constraintFocusOut );
144 QgsFocusWatcher *zWatcher = new QgsFocusWatcher( mZLineEdit );
145 connect( zWatcher, &QgsFocusWatcher::focusOut, this, &QgsAdvancedDigitizingDockWidget::constraintFocusOut );
146 QgsFocusWatcher *mWatcher = new QgsFocusWatcher( mMLineEdit );
147 connect( mWatcher, &QgsFocusWatcher::focusOut, this, &QgsAdvancedDigitizingDockWidget::constraintFocusOut );
148
149 // Common angle snapping menu
150 mCommonAngleActionsMenu = new QMenu( this );
151 // Suppress warning: Potential leak of memory pointed to by 'angleButtonGroup' [clang-analyzer-cplusplus.NewDeleteLeaks]
152#ifndef __clang_analyzer__
153 QActionGroup *angleButtonGroup = new QActionGroup( mCommonAngleActionsMenu ); // actions are exclusive for common angles NOLINT
154#endif
155 QList< QPair< double, QString > > commonAngles;
156 const QList<double> anglesDouble( { 0.0, 0.1, 0.5, 1.0, 5.0, 10.0, 15.0, 18.0, 22.5, 30.0, 45.0, 90.0} );
157 for ( QList<double>::const_iterator it = anglesDouble.constBegin(); it != anglesDouble.constEnd(); ++it )
158 {
159 commonAngles << QPair<double, QString>( *it, formatCommonAngleSnapping( *it ) );
160 }
161
162 {
163 QMenu *snappingPriorityMenu = new QMenu( tr( "Snapping Priority" ), mCommonAngleActionsMenu );
164 QActionGroup *snappingPriorityActionGroup = new QActionGroup( snappingPriorityMenu );
165 QAction *featuresAction = new QAction( tr( "Prioritize Snapping to Features" ), snappingPriorityActionGroup );
166 featuresAction->setCheckable( true );
167 QAction *anglesAction = new QAction( tr( "Prioritize Snapping to Common Angles" ), snappingPriorityActionGroup );
168 anglesAction->setCheckable( true );
169 snappingPriorityActionGroup->addAction( featuresAction );
170 snappingPriorityActionGroup->addAction( anglesAction );
171 snappingPriorityMenu->addAction( anglesAction );
172 snappingPriorityMenu->addAction( featuresAction );
173 connect( anglesAction, &QAction::changed, this, [ = ]
174 {
175 mSnappingPrioritizeFeatures = featuresAction->isChecked();
176 settingsCadSnappingPriorityPrioritizeFeature->setValue( featuresAction->isChecked() );
177 } );
178 featuresAction->setChecked( settingsCadSnappingPriorityPrioritizeFeature->value( ) );
179 anglesAction->setChecked( ! featuresAction->isChecked() );
180 mCommonAngleActionsMenu->addMenu( snappingPriorityMenu );
181 }
182
183
184 for ( QList< QPair<double, QString > >::const_iterator it = commonAngles.constBegin(); it != commonAngles.constEnd(); ++it )
185 {
186 QAction *action = new QAction( it->second, mCommonAngleActionsMenu );
187 action->setCheckable( true );
188 action->setChecked( it->first == mCommonAngleConstraint );
189 mCommonAngleActionsMenu->addAction( action );
190 // Suppress warning: Potential leak of memory pointed to by 'angleButtonGroup' [clang-analyzer-cplusplus.NewDeleteLeaks]
191#ifndef __clang_analyzer__
192 angleButtonGroup->addAction( action );
193#endif
194 mCommonAngleActions.insert( it->first, action );
195 }
196
197 qobject_cast< QToolButton *>( mToolbar->widgetForAction( mSettingsAction ) )->setPopupMode( QToolButton::InstantPopup );
198 mSettingsAction->setMenu( mCommonAngleActionsMenu );
199 mSettingsAction->setCheckable( true );
200 mSettingsAction->setToolTip( "<b>" + tr( "Snap to common angles" ) + "</b><br>(" + tr( "press n to cycle through the options" ) + ")" );
201 mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
202 connect( mCommonAngleActionsMenu, &QMenu::triggered, this, &QgsAdvancedDigitizingDockWidget::settingsButtonTriggered );
203
204 // Construction modes
205 QMenu *constructionMenu = new QMenu( this );
206
207 mLineExtensionAction = new QAction( tr( "Line Extension" ), constructionMenu );
208 mLineExtensionAction->setCheckable( true );
209 constructionMenu->addAction( mLineExtensionAction );
210 connect( mLineExtensionAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
211
212 mXyVertexAction = new QAction( tr( "X/Y Point" ), constructionMenu );
213 mXyVertexAction->setCheckable( true );
214 constructionMenu->addAction( mXyVertexAction );
215 connect( mXyVertexAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
216
217 auto constructionToolBar = qobject_cast< QToolButton *>( mToolbar->widgetForAction( mConstructionAction ) );
218 constructionToolBar->setPopupMode( QToolButton::InstantPopup );
219 constructionToolBar->setMenu( constructionMenu );
220 constructionToolBar->setObjectName( QStringLiteral( "ConstructionButton" ) );
221
222 mConstructionAction->setMenu( mCommonAngleActionsMenu );
223 mConstructionAction->setCheckable( true );
224 mConstructionAction->setToolTip( tr( "Construction Tools" ) );
225// connect( constructionMenu, &QMenu::triggered, this, &QgsAdvancedDigitizingDockWidget::settingsButtonTriggered );
226
227 // set tooltips
228 mConstructionModeAction->setToolTip( "<b>" + tr( "Construction mode" ) + "</b><br>(" + tr( "press c to toggle on/off" ) + ")" );
229 mDistanceLineEdit->setToolTip( "<b>" + tr( "Distance" ) + "</b><br>(" + tr( "press d for quick access" ) + ")" );
230 mLockDistanceButton->setToolTip( "<b>" + tr( "Lock distance" ) + "</b><br>(" + tr( "press Ctrl + d for quick access" ) + ")" );
231 mRepeatingLockDistanceButton->setToolTip( "<b>" + tr( "Continuously lock distance" ) + "</b>" );
232
233 mRelativeAngleButton->setToolTip( "<b>" + tr( "Toggles relative angle to previous segment" ) + "</b><br>(" + tr( "press Shift + a for quick access" ) + ")" );
234 mAngleLineEdit->setToolTip( "<b>" + tr( "Angle" ) + "</b><br>(" + tr( "press a for quick access" ) + ")" );
235 mLockAngleButton->setToolTip( "<b>" + tr( "Lock angle" ) + "</b><br>(" + tr( "press Ctrl + a for quick access" ) + ")" );
236 mRepeatingLockAngleButton->setToolTip( "<b>" + tr( "Continuously lock angle" ) + "</b>" );
237
238 mRelativeXButton->setToolTip( "<b>" + tr( "Toggles relative x to previous node" ) + "</b><br>(" + tr( "press Shift + x for quick access" ) + ")" );
239 mXLineEdit->setToolTip( "<b>" + tr( "X coordinate" ) + "</b><br>(" + tr( "press x for quick access" ) + ")" );
240 mLockXButton->setToolTip( "<b>" + tr( "Lock x coordinate" ) + "</b><br>(" + tr( "press Ctrl + x for quick access" ) + ")" );
241 mRepeatingLockXButton->setToolTip( "<b>" + tr( "Continuously lock x coordinate" ) + "</b>" );
242
243 mRelativeYButton->setToolTip( "<b>" + tr( "Toggles relative y to previous node" ) + "</b><br>(" + tr( "press Shift + y for quick access" ) + ")" );
244 mYLineEdit->setToolTip( "<b>" + tr( "Y coordinate" ) + "</b><br>(" + tr( "press y for quick access" ) + ")" );
245 mLockYButton->setToolTip( "<b>" + tr( "Lock y coordinate" ) + "</b><br>(" + tr( "press Ctrl + y for quick access" ) + ")" );
246 mRepeatingLockYButton->setToolTip( "<b>" + tr( "Continuously lock y coordinate" ) + "</b>" );
247
248 mRelativeZButton->setToolTip( "<b>" + tr( "Toggles relative z to previous node" ) + "</b><br>(" + tr( "press Shift + z for quick access" ) + ")" );
249 mZLineEdit->setToolTip( "<b>" + tr( "Z coordinate" ) + "</b><br>(" + tr( "press z for quick access" ) + ")" );
250 mLockZButton->setToolTip( "<b>" + tr( "Lock z coordinate" ) + "</b><br>(" + tr( "press Ctrl + z for quick access" ) + ")" );
251 mRepeatingLockZButton->setToolTip( "<b>" + tr( "Continuously lock z coordinate" ) + "</b>" );
252
253 mRelativeMButton->setToolTip( "<b>" + tr( "Toggles relative m to previous node" ) + "</b><br>(" + tr( "press Shift + m for quick access" ) + ")" );
254 mMLineEdit->setToolTip( "<b>" + tr( "M coordinate" ) + "</b><br>(" + tr( "press m for quick access" ) + ")" );
255 mLockMButton->setToolTip( "<b>" + tr( "Lock m coordinate" ) + "</b><br>(" + tr( "press Ctrl + m for quick access" ) + ")" );
256 mRepeatingLockMButton->setToolTip( "<b>" + tr( "Continuously lock m coordinate" ) + "</b>" );
257
258 // Create the slots/signals
259 connect( mXLineEdit, &QLineEdit::textChanged, this, &QgsAdvancedDigitizingDockWidget::valueXChanged );
260 connect( mYLineEdit, &QLineEdit::textChanged, this, &QgsAdvancedDigitizingDockWidget::valueYChanged );
261 connect( mZLineEdit, &QLineEdit::textChanged, this, &QgsAdvancedDigitizingDockWidget::valueZChanged );
262 connect( mMLineEdit, &QLineEdit::textChanged, this, &QgsAdvancedDigitizingDockWidget::valueMChanged );
263 connect( mDistanceLineEdit, &QLineEdit::textChanged, this, &QgsAdvancedDigitizingDockWidget::valueDistanceChanged );
264 connect( mAngleLineEdit, &QLineEdit::textChanged, this, &QgsAdvancedDigitizingDockWidget::valueAngleChanged );
265
266 // Create the floater
267 mFloaterActionsMenu = new QMenu( this );
268 qobject_cast< QToolButton *>( mToolbar->widgetForAction( mFloaterAction ) )->setPopupMode( QToolButton::InstantPopup );
269 mFloaterAction->setMenu( mFloaterActionsMenu );
270 mFloaterAction->setCheckable( true );
271 mFloater = new QgsAdvancedDigitizingFloater( canvas, this );
272 mFloaterAction->setChecked( mFloater->active() );
273
274 // Add floater config actions
275 {
276 QAction *action = new QAction( tr( "Show floater" ), mFloaterActionsMenu );
277 action->setCheckable( true );
278 action->setChecked( mFloater->active() );
279 mFloaterActionsMenu->addAction( action );
280 connect( action, &QAction::toggled, this, [ = ]( bool checked )
281 {
282 mFloater->setActive( checked );
283 mFloaterAction->setChecked( checked );
284 } );
285 }
286
287 mFloaterActionsMenu->addSeparator();
288
289 {
290 QAction *action = new QAction( tr( "Show distance" ), mFloaterActionsMenu );
291 action->setCheckable( true );
292 mFloaterActionsMenu->addAction( action );
293 connect( action, &QAction::toggled, this, [ = ]( bool checked )
294 {
296 } );
297 action->setChecked( QgsSettings().value( QStringLiteral( "/Cad/DistanceShowInFloater" ), true ).toBool() );
298 }
299
300 {
301 QAction *action = new QAction( tr( "Show angle" ), mFloaterActionsMenu );
302 action->setCheckable( true );
303 mFloaterActionsMenu->addAction( action );
304 connect( action, &QAction::toggled, this, [ = ]( bool checked )
305 {
307 } );
308 action->setChecked( QgsSettings().value( QStringLiteral( "/Cad/AngleShowInFloater" ), true ).toBool() );
309 }
310
311 {
312 QAction *action = new QAction( tr( "Show XY coordinates" ), mFloaterActionsMenu );
313 action->setCheckable( true );
314 mFloaterActionsMenu->addAction( action );
315 connect( action, &QAction::toggled, this, [ = ]( bool checked )
316 {
319 } );
320 // There is no separate menu option for X and Y so let's check for X only.
321 action->setChecked( QgsSettings().value( QStringLiteral( "/Cad/XCoordinateShowInFloater" ), true ).toBool() );
322 }
323
324 {
325 QAction *action = new QAction( tr( "Show Z value" ), mFloaterActionsMenu );
326 action->setCheckable( true );
327 mFloaterActionsMenu->addAction( action );
328 connect( action, &QAction::toggled, this, [ = ]( bool checked )
329 {
331 } );
332 action->setChecked( QgsSettings().value( QStringLiteral( "/Cad/ZCoordinateShowInFloater" ), true ).toBool() );
333 }
334
335 {
336 QAction *action = new QAction( tr( "Show M value" ), mFloaterActionsMenu );
337 action->setCheckable( true );
338 mFloaterActionsMenu->addAction( action );
339 connect( action, &QAction::toggled, this, [ = ]( bool checked )
340 {
342 } );
343 action->setChecked( QgsSettings().value( QStringLiteral( "/Cad/MCoordinateShowInFloater" ), true ).toBool() );
344 }
345
346 {
347 QAction *action = new QAction( tr( "Show bearing/azimuth" ), mFloaterActionsMenu );
348 action->setCheckable( true );
349 mFloaterActionsMenu->addAction( action );
350 connect( action, &QAction::toggled, this, [ = ]( bool checked )
351 {
353 } );
354 action->setChecked( QgsSettings().value( QStringLiteral( "/Cad/BearingShowInFloater" ), false ).toBool() );
355 }
356
357 {
358 QAction *action = new QAction( tr( "Show common snapping angle" ), mFloaterActionsMenu );
359 action->setCheckable( true );
360 mFloaterActionsMenu->addAction( action );
361 connect( action, &QAction::toggled, this, [ = ]( bool checked )
362 {
364 } );
365 action->setChecked( QgsSettings().value( QStringLiteral( "/Cad/CommonAngleSnappingShowInFloater" ), false ).toBool() );
366 }
367
368 updateCapacity( true );
369 connect( QgsProject::instance(), &QgsProject::snappingConfigChanged, this, [ = ] { updateCapacity( true ); } );
370
371 disable();
372}
373
375{
376 if ( angle == 0 )
377 return tr( "Do Not Snap to Common Angles" );
378 else
379 return QString( tr( "%1, %2, %3, %4°…" ) ).arg( angle, 0, 'f', 1 ).arg( angle * 2, 0, 'f', 1 ).arg( angle * 3, 0, 'f', 1 ).arg( angle * 4, 0, 'f', 1 );
380}
381
382void QgsAdvancedDigitizingDockWidget::setX( const QString &value, WidgetSetMode mode )
383{
384 mXLineEdit->setText( value );
385 if ( mode == WidgetSetMode::ReturnPressed )
386 {
387 emit mXLineEdit->returnPressed();
388 }
389 else if ( mode == WidgetSetMode::FocusOut )
390 {
391 QEvent *e = new QEvent( QEvent::FocusOut );
392 QCoreApplication::postEvent( mXLineEdit, e );
393 }
394 else if ( mode == WidgetSetMode::TextEdited )
395 {
396 emit mXLineEdit->textEdited( value );
397 }
398}
399void QgsAdvancedDigitizingDockWidget::setY( const QString &value, WidgetSetMode mode )
400{
401 mYLineEdit->setText( value );
402 if ( mode == WidgetSetMode::ReturnPressed )
403 {
404 emit mYLineEdit->returnPressed();
405 }
406 else if ( mode == WidgetSetMode::FocusOut )
407 {
408 QEvent *e = new QEvent( QEvent::FocusOut );
409 QCoreApplication::postEvent( mYLineEdit, e );
410 }
411 else if ( mode == WidgetSetMode::TextEdited )
412 {
413 emit mYLineEdit->textEdited( value );
414 }
415}
416void QgsAdvancedDigitizingDockWidget::setZ( const QString &value, WidgetSetMode mode )
417{
418 mZLineEdit->setText( value );
419 if ( mode == WidgetSetMode::ReturnPressed )
420 {
421 emit mZLineEdit->returnPressed();
422 }
423 else if ( mode == WidgetSetMode::FocusOut )
424 {
425 QEvent *e = new QEvent( QEvent::FocusOut );
426 QCoreApplication::postEvent( mZLineEdit, e );
427 }
428 else if ( mode == WidgetSetMode::TextEdited )
429 {
430 emit mZLineEdit->textEdited( value );
431 }
432}
433void QgsAdvancedDigitizingDockWidget::setM( const QString &value, WidgetSetMode mode )
434{
435 mMLineEdit->setText( value );
436 if ( mode == WidgetSetMode::ReturnPressed )
437 {
438 emit mMLineEdit->returnPressed();
439 }
440 else if ( mode == WidgetSetMode::FocusOut )
441 {
442 QEvent *e = new QEvent( QEvent::FocusOut );
443 QCoreApplication::postEvent( mMLineEdit, e );
444 }
445 else if ( mode == WidgetSetMode::TextEdited )
446 {
447 emit mMLineEdit->textEdited( value );
448 }
449}
451{
452 mAngleLineEdit->setText( value );
453 if ( mode == WidgetSetMode::ReturnPressed )
454 {
455 emit mAngleLineEdit->returnPressed();
456 }
457 else if ( mode == WidgetSetMode::FocusOut )
458 {
459 emit mAngleLineEdit->textEdited( value );
460 }
461}
463{
464 mDistanceLineEdit->setText( value );
465 if ( mode == WidgetSetMode::ReturnPressed )
466 {
467 emit mDistanceLineEdit->returnPressed();
468 }
469 else if ( mode == WidgetSetMode::FocusOut )
470 {
471 QEvent *e = new QEvent( QEvent::FocusOut );
472 QCoreApplication::postEvent( mDistanceLineEdit, e );
473 }
474 else if ( mode == WidgetSetMode::TextEdited )
475 {
476 emit mDistanceLineEdit->textEdited( value );
477 }
478}
479
480
481void QgsAdvancedDigitizingDockWidget::setCadEnabled( bool enabled )
482{
483 mCadEnabled = enabled;
484 mEnableAction->setChecked( enabled );
485 mConstructionModeAction->setEnabled( enabled );
486 mSettingsAction->setEnabled( enabled );
487 mInputWidgets->setEnabled( enabled );
488 mFloaterAction->setEnabled( enabled );
489 mConstructionAction->setEnabled( enabled );
490
491 if ( !enabled )
492 {
493 // uncheck at deactivation
494 mLineExtensionAction->setChecked( false );
495 mXyVertexAction->setChecked( false );
496 // will be reactivated in updateCapacities
497 mParallelAction->setEnabled( false );
498 mPerpendicularAction->setEnabled( false );
499 }
500
501
502 clear();
504 setConstructionMode( false );
505
506 switchZM();
507 emit cadEnabledChanged( enabled );
508
509 if ( enabled )
510 {
511 emit valueCommonAngleSnappingChanged( mCommonAngleConstraint );
512 }
513
514 mLastSnapMatch = QgsPointLocator::Match();
515}
516
517
519{
520 bool enableZ = false;
521 bool enableM = false;
522
523 if ( QgsMapLayer *layer = targetLayer() )
524 {
525 switch ( layer->type() )
526 {
528 {
529 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
530 const Qgis::WkbType type = vlayer->wkbType();
531 enableZ = QgsWkbTypes::hasZ( type );
532 enableM = QgsWkbTypes::hasM( type );
533 break;
534 }
535
537 {
538 QgsMeshLayer *mlayer = qobject_cast<QgsMeshLayer *>( layer );
539 enableZ = mlayer->isEditable();
540 break;
541 }
542
550 break;
551 }
552 }
553
554 setEnabledZ( enableZ );
555 setEnabledM( enableM );
556}
557
559{
560 mRelativeZButton->setEnabled( enable );
561 mZLabel->setEnabled( enable );
562 mZLineEdit->setEnabled( enable );
563 if ( mZLineEdit->isEnabled() )
564 mZLineEdit->setText( QLocale().toString( QgsMapToolEdit::defaultZValue(), 'f', 6 ) );
565 else
566 mZLineEdit->clear();
567 mLockZButton->setEnabled( enable );
568 emit enabledChangedZ( enable );
569}
570
572{
573 mRelativeMButton->setEnabled( enable );
574 mMLabel->setEnabled( enable );
575 mMLineEdit->setEnabled( enable );
576 if ( mMLineEdit->isEnabled() )
577 mMLineEdit->setText( QLocale().toString( QgsMapToolEdit::defaultMValue(), 'f', 6 ) );
578 else
579 mMLineEdit->clear();
580 mLockMButton->setEnabled( enable );
581 emit enabledChangedM( enable );
582}
583
584void QgsAdvancedDigitizingDockWidget::activateCad( bool enabled )
585{
586 enabled &= mCurrentMapToolSupportsCad;
587
588 mSessionActive = enabled;
589
590 if ( enabled && !isVisible() )
591 {
592 show();
593 }
594
595 setCadEnabled( enabled );
596}
597
598void QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked( bool activated )
599{
600 if ( !activated )
601 {
602 lockBetweenLineConstraint( Qgis::BetweenLineConstraint::NoConstraint );
603 }
604 else if ( sender() == mParallelAction )
605 {
606 lockBetweenLineConstraint( Qgis::BetweenLineConstraint::Parallel );
607 }
608 else if ( sender() == mPerpendicularAction )
609 {
610 lockBetweenLineConstraint( Qgis::BetweenLineConstraint::Perpendicular );
611 }
612}
613
614void QgsAdvancedDigitizingDockWidget::setConstraintRelative( bool activate )
615{
616 if ( sender() == mRelativeAngleButton )
617 {
618 mAngleConstraint->setRelative( activate );
619 emit relativeAngleChanged( activate );
620 }
621 else if ( sender() == mRelativeXButton )
622 {
623 mXConstraint->setRelative( activate );
624 emit relativeXChanged( activate );
625 }
626 else if ( sender() == mRelativeYButton )
627 {
628 mYConstraint->setRelative( activate );
629 emit relativeYChanged( activate );
630 }
631 else if ( sender() == mRelativeZButton )
632 {
633 mZConstraint->setRelative( activate );
634 emit relativeZChanged( activate );
635 }
636 else if ( sender() == mRelativeMButton )
637 {
638 mMConstraint->setRelative( activate );
639 emit relativeMChanged( activate );
640 }
641}
642
643void QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock( bool activate )
644{
645 if ( sender() == mRepeatingLockDistanceButton )
646 {
647 mDistanceConstraint->setRepeatingLock( activate );
648 }
649 else if ( sender() == mRepeatingLockAngleButton )
650 {
651 mAngleConstraint->setRepeatingLock( activate );
652 }
653 else if ( sender() == mRepeatingLockXButton )
654 {
655 mXConstraint->setRepeatingLock( activate );
656 }
657 else if ( sender() == mRepeatingLockYButton )
658 {
659 mYConstraint->setRepeatingLock( activate );
660 }
661 else if ( sender() == mRepeatingLockZButton )
662 {
663 mZConstraint->setRepeatingLock( activate );
664 }
665 else if ( sender() == mRepeatingLockMButton )
666 {
667 mMConstraint->setRepeatingLock( activate );
668 }
669}
670
671void QgsAdvancedDigitizingDockWidget::setConstructionMode( bool enabled )
672{
673 mConstructionMode = enabled;
674 mConstructionModeAction->setChecked( enabled );
675}
676
677void QgsAdvancedDigitizingDockWidget::settingsButtonTriggered( QAction *action )
678{
679 // common angles
680 for ( auto it = mCommonAngleActions.cbegin(); it != mCommonAngleActions.cend(); ++it )
681 {
682 if ( it.value() == action )
683 {
684 it.value()->setChecked( true );
685 mCommonAngleConstraint = it.key();
686 QgsSettings().setValue( QStringLiteral( "/Cad/CommonAngle" ), it.key() );
687 mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
688 emit valueCommonAngleSnappingChanged( mCommonAngleConstraint );
689 return;
690 }
691 }
692}
693
694QgsMapLayer *QgsAdvancedDigitizingDockWidget::targetLayer() const
695{
696 if ( QgsMapToolAdvancedDigitizing *advancedTool = qobject_cast< QgsMapToolAdvancedDigitizing * >( mMapCanvas->mapTool() ) )
697 {
698 return advancedTool->layer();
699 }
700 else
701 {
702 return mMapCanvas->currentLayer();
703 }
704}
705
706void QgsAdvancedDigitizingDockWidget::releaseLocks( bool releaseRepeatingLocks )
707{
708 // release all locks except construction mode
709
710 lockBetweenLineConstraint( Qgis::BetweenLineConstraint::NoConstraint );
711
712 if ( releaseRepeatingLocks )
713 {
714 mXyVertexAction->setChecked( false );
715 mXyVertexConstraint->setLockMode( CadConstraint::NoLock );
716 emit softLockXyChanged( false );
717
718 mLineExtensionAction->setChecked( false );
719 mLineExtensionConstraint->setLockMode( CadConstraint::NoLock );
720 emit softLockLineExtensionChanged( false );
721
723 }
724
725 if ( releaseRepeatingLocks || !mAngleConstraint->isRepeatingLock() )
726 {
727 mAngleConstraint->setLockMode( CadConstraint::NoLock );
728 emit lockAngleChanged( false );
729 }
730 if ( releaseRepeatingLocks || !mDistanceConstraint->isRepeatingLock() )
731 {
732 mDistanceConstraint->setLockMode( CadConstraint::NoLock );
733 emit lockDistanceChanged( false );
734 }
735 if ( releaseRepeatingLocks || !mXConstraint->isRepeatingLock() )
736 {
737 mXConstraint->setLockMode( CadConstraint::NoLock );
738 emit lockXChanged( false );
739 }
740 if ( releaseRepeatingLocks || !mYConstraint->isRepeatingLock() )
741 {
742 mYConstraint->setLockMode( CadConstraint::NoLock );
743 emit lockYChanged( false );
744 }
745 if ( releaseRepeatingLocks || !mZConstraint->isRepeatingLock() )
746 {
747 mZConstraint->setLockMode( CadConstraint::NoLock );
748 emit lockZChanged( false );
749 }
750 if ( releaseRepeatingLocks || !mMConstraint->isRepeatingLock() )
751 {
752 mMConstraint->setLockMode( CadConstraint::NoLock );
753 emit lockMChanged( false );
754 }
755
756 if ( !mCadPointList.empty() )
757 {
758 if ( !mXConstraint->isLocked() && !mXConstraint->relative() )
759 {
760 mXConstraint->setValue( mCadPointList.constLast().x(), true );
761 }
762 if ( !mYConstraint->isLocked() && !mYConstraint->relative() )
763 {
764 mYConstraint->setValue( mCadPointList.constLast().y(), true );
765 }
766 if ( !mZConstraint->isLocked() && !mZConstraint->relative() )
767 {
768 mZConstraint->setValue( mCadPointList.constLast().z(), true );
769 }
770 if ( !mMConstraint->isLocked() && !mMConstraint->relative() )
771 {
772 mMConstraint->setValue( mCadPointList.constLast().m(), true );
773 }
774 }
775}
776
777#if 0
778void QgsAdvancedDigitizingDockWidget::emit pointChanged()
779{
780 // run a fake map mouse event to update the paint item
781 QPoint globalPos = mMapCanvas->cursor().pos();
782 QPoint pos = mMapCanvas->mapFromGlobal( globalPos );
783 QMouseEvent *e = new QMouseEvent( QEvent::MouseMove, pos, globalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier );
784 mCurrentMapTool->canvasMoveEvent( e );
785}
786#endif
787
788QgsAdvancedDigitizingDockWidget::CadConstraint *QgsAdvancedDigitizingDockWidget::objectToConstraint( const QObject *obj ) const
789{
790 CadConstraint *constraint = nullptr;
791 if ( obj == mAngleLineEdit || obj == mLockAngleButton )
792 {
793 constraint = mAngleConstraint.get();
794 }
795 else if ( obj == mDistanceLineEdit || obj == mLockDistanceButton )
796 {
797 constraint = mDistanceConstraint.get();
798 }
799 else if ( obj == mXLineEdit || obj == mLockXButton )
800 {
801 constraint = mXConstraint.get();
802 }
803 else if ( obj == mYLineEdit || obj == mLockYButton )
804 {
805 constraint = mYConstraint.get();
806 }
807 else if ( obj == mZLineEdit || obj == mLockZButton )
808 {
809 constraint = mZConstraint.get();
810 }
811 else if ( obj == mMLineEdit || obj == mLockMButton )
812 {
813 constraint = mMConstraint.get();
814 }
815 else if ( obj == mLineExtensionAction )
816 {
817 constraint = mLineExtensionConstraint.get();
818 }
819 else if ( obj == mXyVertexAction )
820 {
821 constraint = mXyVertexConstraint.get();
822 }
823 return constraint;
824}
825
826double QgsAdvancedDigitizingDockWidget::parseUserInput( const QString &inputValue, const Qgis::CadConstraintType type, bool &ok ) const
827{
828 ok = false;
829
830 const QString cleanedInputValue { CadConstraint::removeSuffix( inputValue, type ) };
831 double value = qgsPermissiveToDouble( cleanedInputValue, ok );
832
833 if ( ! ok )
834 {
835 // try to evaluate expression
836 QgsExpression expr( inputValue );
837 const QVariant result = expr.evaluate();
838 if ( expr.hasEvalError() )
839 {
840 ok = false;
841 QString inputValueC { inputValue };
842
843 // First: try removing group separator
844 if ( inputValue.contains( QLocale().groupSeparator() ) )
845 {
846 inputValueC.remove( QLocale().groupSeparator() );
847 QgsExpression exprC( inputValueC );
848 const QVariant resultC = exprC.evaluate();
849 if ( ! exprC.hasEvalError() )
850 {
851 value = resultC.toDouble( &ok );
852 }
853 }
854
855 // Second: be nice with non-dot locales
856 if ( !ok && QLocale().decimalPoint() != QChar( '.' ) && inputValueC.contains( QLocale().decimalPoint() ) )
857 {
858 QgsExpression exprC( inputValueC .replace( QLocale().decimalPoint(), QChar( '.' ) ) );
859 const QVariant resultC = exprC.evaluate();
860 if ( ! exprC.hasEvalError() )
861 {
862 value = resultC.toDouble( &ok );
863 }
864 }
865 }
866 else
867 {
868 value = result.toDouble( &ok );
869 }
870 }
871 return value;
872}
873
874void QgsAdvancedDigitizingDockWidget::updateConstraintValue( CadConstraint *constraint, const QString &textValue, bool convertExpression )
875{
876 if ( !constraint || textValue.isEmpty() )
877 {
878 return;
879 }
880
881 if ( constraint->lockMode() == CadConstraint::NoLock )
882 return;
883
884 bool ok;
885 const double value = parseUserInput( textValue, constraint->cadConstraintType(), ok );
886 if ( !ok )
887 return;
888
889 constraint->setValue( value, convertExpression );
890 // run a fake map mouse event to update the paint item
891 emit pointChangedV2( mCadPointList.value( 0 ) );
892}
893
894void QgsAdvancedDigitizingDockWidget::lockConstraint( bool activate /* default true */ )
895{
896 CadConstraint *constraint = objectToConstraint( sender() );
897 if ( !constraint )
898 {
899 return;
900 }
901
902 if ( activate )
903 {
904 const QString textValue = constraint->lineEdit()->text();
905 if ( !textValue.isEmpty() )
906 {
907 bool ok;
908 const double value = parseUserInput( textValue, constraint->cadConstraintType(), ok );
909 if ( ok )
910 {
911 constraint->setValue( value );
912 }
913 else
914 {
915 activate = false;
916 }
917 }
918 else
919 {
920 activate = false;
921 }
922 }
923 constraint->setLockMode( activate ? CadConstraint::HardLock : CadConstraint::NoLock );
924
925 if ( constraint == mXConstraint.get() )
926 {
927 emit lockXChanged( activate );
928 }
929 else if ( constraint == mYConstraint.get() )
930 {
931 emit lockYChanged( activate );
932 }
933 else if ( constraint == mZConstraint.get() )
934 {
935 emit lockZChanged( activate );
936 }
937 else if ( constraint == mMConstraint.get() )
938 {
939 emit lockMChanged( activate );
940 }
941 else if ( constraint == mDistanceConstraint.get() )
942 {
943 emit lockDistanceChanged( activate );
944 }
945 else if ( constraint == mAngleConstraint.get() )
946 {
947 emit lockAngleChanged( activate );
948 }
949
950 if ( activate )
951 {
952 // deactivate perpendicular/parallel if angle has been activated
953 if ( constraint == mAngleConstraint.get() )
954 {
955 lockBetweenLineConstraint( Qgis::BetweenLineConstraint::NoConstraint );
956 }
957
958 // run a fake map mouse event to update the paint item
959 emit pointChangedV2( mCadPointList.value( 0 ) );
960 }
961}
962
963void QgsAdvancedDigitizingDockWidget::constraintTextEdited( const QString &textValue )
964{
965 CadConstraint *constraint = objectToConstraint( sender() );
966 if ( !constraint )
967 {
968 return;
969 }
970
971 updateConstraintValue( constraint, textValue, false );
972}
973
974void QgsAdvancedDigitizingDockWidget::constraintFocusOut()
975{
976 QLineEdit *lineEdit = qobject_cast< QLineEdit * >( sender()->parent() );
977 if ( !lineEdit )
978 return;
979
980 CadConstraint *constraint = objectToConstraint( lineEdit );
981 if ( !constraint )
982 {
983 return;
984 }
985
986 updateConstraintValue( constraint, lineEdit->text(), true );
987}
988
989void QgsAdvancedDigitizingDockWidget::lockBetweenLineConstraint( Qgis::BetweenLineConstraint constraint )
990{
991 mBetweenLineConstraint = constraint;
992 mPerpendicularAction->setChecked( constraint == Qgis::BetweenLineConstraint::Perpendicular );
993 mParallelAction->setChecked( constraint == Qgis::BetweenLineConstraint::Parallel );
994}
995
996void QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint( bool activate /* default true */ )
997{
998 CadConstraint *constraint = objectToConstraint( sender() );
999 if ( !constraint )
1000 {
1001 return;
1002 }
1003
1004 constraint->setLockMode( activate ? CadConstraint::SoftLock : CadConstraint::NoLock );
1005
1006 if ( constraint == mXyVertexConstraint.get() )
1007 {
1008 emit softLockXyChanged( activate );
1009 }
1010 else if ( constraint == mLineExtensionConstraint.get() )
1011 {
1012 emit softLockLineExtensionChanged( activate );
1013 }
1014
1015 if ( activate )
1016 {
1017 // run a fake map mouse event to update the paint item
1018 emit pointChangedV2( mCadPointList.value( 0 ) );
1019 }
1020
1021 clearLockedSnapVertices( false );
1022}
1023
1024void QgsAdvancedDigitizingDockWidget::updateCapacity( bool updateUIwithoutChange )
1025{
1026 CadCapacities newCapacities = CadCapacities();
1027 const bool isGeographic = mMapCanvas->mapSettings().destinationCrs().isGeographic();
1028
1029 // first point is the mouse point (it doesn't count)
1030 if ( mCadPointList.count() > 1 )
1031 {
1032 newCapacities |= RelativeCoordinates;
1033 if ( !isGeographic )
1034 {
1035 newCapacities |= AbsoluteAngle;
1036 newCapacities |= Distance;
1037 }
1038 }
1039 if ( mCadPointList.count() > 2 )
1040 {
1041 if ( !isGeographic )
1042 newCapacities |= RelativeAngle;
1043 }
1044 if ( !updateUIwithoutChange && newCapacities == mCapacities )
1045 {
1046 return;
1047 }
1048
1049 const bool snappingEnabled = QgsProject::instance()->snappingConfig().enabled();
1050
1051 // update the UI according to new capacities
1052 // still keep the old to compare
1053
1054 const bool distance = mCadEnabled && newCapacities.testFlag( Distance );
1055 const bool relativeAngle = mCadEnabled && newCapacities.testFlag( RelativeAngle );
1056 const bool absoluteAngle = mCadEnabled && newCapacities.testFlag( AbsoluteAngle );
1057 const bool relativeCoordinates = mCadEnabled && newCapacities.testFlag( RelativeCoordinates );
1058
1059 mPerpendicularAction->setEnabled( distance && snappingEnabled );
1060 mParallelAction->setEnabled( distance && snappingEnabled );
1061
1062 mLineExtensionAction->setEnabled( snappingEnabled );
1063 mXyVertexAction->setEnabled( snappingEnabled );
1064 clearLockedSnapVertices( false );
1065
1066 //update tooltips on buttons
1067 if ( !snappingEnabled )
1068 {
1069 mPerpendicularAction->setToolTip( tr( "Snapping must be enabled to utilize perpendicular mode." ) );
1070 mParallelAction->setToolTip( tr( "Snapping must be enabled to utilize parallel mode." ) );
1071 mLineExtensionAction->setToolTip( tr( "Snapping must be enabled to utilize line extension mode." ) );
1072 mXyVertexAction->setToolTip( tr( "Snapping must be enabled to utilize xy point mode." ) );
1073 }
1074 else if ( mCadPointList.count() <= 1 )
1075 {
1076 mPerpendicularAction->setToolTip( tr( "A first vertex should be drawn to utilize perpendicular mode." ) );
1077 mParallelAction->setToolTip( tr( "A first vertex should be drawn to utilize parallel mode." ) );
1078 }
1079 else if ( isGeographic )
1080 {
1081 mPerpendicularAction->setToolTip( tr( "Perpendicular mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1082 mParallelAction->setToolTip( tr( "Parallel mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1083 }
1084 else
1085 {
1086 mPerpendicularAction->setToolTip( "<b>" + tr( "Perpendicular" ) + "</b><br>(" + tr( "press p to switch between perpendicular, parallel and normal mode" ) + ")" );
1087 mParallelAction->setToolTip( "<b>" + tr( "Parallel" ) + "</b><br>(" + tr( "press p to switch between perpendicular, parallel and normal mode" ) + ")" );
1088 }
1089
1090
1091 if ( !absoluteAngle )
1092 {
1093 lockBetweenLineConstraint( Qgis::BetweenLineConstraint::NoConstraint );
1094 }
1095
1096 // absolute angle = azimuth, relative = from previous line
1097 mLockAngleButton->setEnabled( absoluteAngle );
1098 mRelativeAngleButton->setEnabled( relativeAngle );
1099 mAngleLineEdit->setEnabled( absoluteAngle );
1100 emit enabledChangedAngle( absoluteAngle );
1101 if ( !absoluteAngle )
1102 {
1103 mAngleConstraint->setLockMode( CadConstraint::NoLock );
1104 }
1105 if ( !relativeAngle )
1106 {
1107 mAngleConstraint->setRelative( false );
1108 emit relativeAngleChanged( false );
1109 }
1110 else if ( relativeAngle && !mCapacities.testFlag( RelativeAngle ) )
1111 {
1112 // set angle mode to relative if can do and wasn't available before
1113 mAngleConstraint->setRelative( true );
1114 emit relativeAngleChanged( true );
1115 }
1116
1117 // distance is always relative
1118 mLockDistanceButton->setEnabled( distance && relativeCoordinates );
1119 mDistanceLineEdit->setEnabled( distance && relativeCoordinates );
1120 emit enabledChangedDistance( distance && relativeCoordinates );
1121 if ( !( distance && relativeCoordinates ) )
1122 {
1123 mDistanceConstraint->setLockMode( CadConstraint::NoLock );
1124 }
1125
1126 mRelativeXButton->setEnabled( relativeCoordinates );
1127 mRelativeYButton->setEnabled( relativeCoordinates );
1128 mRelativeZButton->setEnabled( relativeCoordinates );
1129 mRelativeMButton->setEnabled( relativeCoordinates );
1130
1131 // update capacities
1132 mCapacities = newCapacities;
1133 mCadPaintItem->updatePosition();
1134}
1135
1136
1138{
1140 constr.locked = c->isLocked();
1141 constr.relative = c->relative();
1142 constr.value = c->value();
1143 return constr;
1144}
1145
1146void QgsAdvancedDigitizingDockWidget::toggleLockedSnapVertex( const QgsPointLocator::Match &snapMatch, QgsPointLocator::Match previouslySnap )
1147{
1148 // do nothing if not activated
1149 if ( !mLineExtensionConstraint->isLocked() && !mXyVertexConstraint->isLocked() )
1150 {
1151 return;
1152 }
1153
1154 // if the first is same actual, not toggle if previously snapped
1155 const int lastIndex = mLockedSnapVertices.length() - 1;
1156 for ( int i = lastIndex ; i >= 0; --i )
1157 {
1158 if ( mLockedSnapVertices[i].point() == snapMatch.point() )
1159 {
1160 if ( snapMatch.point() != previouslySnap.point() )
1161 {
1162 mLockedSnapVertices.removeAt( i );
1163 }
1164 return;
1165 }
1166 }
1167
1168 if ( snapMatch.point() != previouslySnap.point() )
1169 {
1170 mLockedSnapVertices.enqueue( snapMatch );
1171 }
1172
1173 if ( mLockedSnapVertices.count() > 3 )
1174 {
1175 mLockedSnapVertices.dequeue();
1176 }
1177}
1178
1180{
1182 context.snappingUtils = mMapCanvas->snappingUtils();
1183 context.mapUnitsPerPixel = mMapCanvas->mapUnitsPerPixel();
1184 context.xConstraint = _constraint( mXConstraint.get() );
1185 context.yConstraint = _constraint( mYConstraint.get() );
1186 context.zConstraint = _constraint( mZConstraint.get() );
1187 context.mConstraint = _constraint( mMConstraint.get() );
1188 context.distanceConstraint = _constraint( mDistanceConstraint.get() );
1189 context.angleConstraint = _constraint( mAngleConstraint.get() );
1190 context.snappingToFeaturesOverridesCommonAngle = mSnappingPrioritizeFeatures;
1191
1192 context.lineExtensionConstraint = _constraint( mLineExtensionConstraint.get() );
1193 context.xyVertexConstraint = _constraint( mXyVertexConstraint.get() );
1194
1195 context.setCadPoints( mCadPointList );
1196 context.setLockedSnapVertices( mLockedSnapVertices );
1197
1200 context.commonAngleConstraint.value = mCommonAngleConstraint;
1201
1203
1204 const bool res = output.valid;
1205 QgsPoint point = pointXYToPoint( output.finalMapPoint );
1206 mSnappedSegment.clear();
1207 if ( output.snapMatch.hasEdge() )
1208 {
1209 QgsPointXY edgePt0, edgePt1;
1210 output.snapMatch.edgePoints( edgePt0, edgePt1 );
1211 mSnappedSegment << edgePt0 << edgePt1;
1212 }
1213 if ( mAngleConstraint->lockMode() != CadConstraint::HardLock )
1214 {
1215 if ( output.softLockCommonAngle != -1 )
1216 {
1217 mAngleConstraint->setLockMode( CadConstraint::SoftLock );
1218 mAngleConstraint->setValue( output.softLockCommonAngle );
1219 }
1220 else
1221 {
1222 mAngleConstraint->setLockMode( CadConstraint::NoLock );
1223 }
1224 }
1225
1226 mSoftLockLineExtension = output.softLockLineExtension;
1227 mSoftLockX = output.softLockX;
1228 mSoftLockY = output.softLockY;
1229
1230 if ( output.snapMatch.isValid() )
1231 {
1232 mSnapIndicator->setMatch( output.snapMatch );
1233 mSnapIndicator->setVisible( true );
1234 }
1235 else
1236 {
1237 mSnapIndicator->setVisible( false );
1238 }
1239
1240 /*
1241 * Ensure that Z and M are passed
1242 * It will be dropped as needed later.
1243 */
1246
1247 /*
1248 * Constraints are applied in 2D, they are always called when using the tool
1249 * but they do not take into account if when you snap on a vertex it has
1250 * a Z value.
1251 * To get the value we use the snapPoint method. However, we only apply it
1252 * when the snapped point corresponds to the constrained point or on an edge
1253 * if the topological editing is activated. Also, we don't apply it if
1254 * the point is not linked to a layer.
1255 */
1256 e->setMapPoint( point );
1257 mSnapMatch = context.snappingUtils->snapToMap( point, nullptr, true );
1258 if ( mSnapMatch.layer() )
1259 {
1260 if ( ( ( mSnapMatch.hasVertex() || mSnapMatch.hasLineEndpoint() ) && ( point == mSnapMatch.point() ) )
1261 || ( mSnapMatch.hasEdge() && QgsProject::instance()->topologicalEditing() ) )
1262 {
1263 e->snapPoint();
1264 point = mSnapMatch.interpolatedPoint( mMapCanvas->mapSettings().destinationCrs() );
1265 }
1266 }
1267
1268 if ( mSnapMatch.hasVertex() || mSnapMatch.hasLineEndpoint() )
1269 {
1270 toggleLockedSnapVertex( mSnapMatch, mLastSnapMatch );
1271 mLastSnapMatch = mSnapMatch;
1272 }
1273 else
1274 {
1275 mLastSnapMatch = QgsPointLocator::Match();
1276 }
1277
1278 /*
1279 * And if M or Z lock button is activated get the value of the input.
1280 */
1281 if ( mLockZButton->isChecked() )
1282 {
1283 point.setZ( QLocale().toDouble( mZLineEdit->text() ) );
1284 }
1285 if ( mLockMButton->isChecked() )
1286 {
1287 point.setM( QLocale().toDouble( mMLineEdit->text() ) );
1288 }
1289
1290 // update the point list
1291 updateCurrentPoint( point );
1292
1293 updateUnlockedConstraintValues( point );
1294
1295 if ( res )
1296 {
1297 emit popWarning();
1298 }
1299 else
1300 {
1301 emit pushWarning( tr( "Some constraints are incompatible. Resulting point might be incorrect." ) );
1302 }
1303
1304 return res;
1305}
1306
1307
1308void QgsAdvancedDigitizingDockWidget::updateUnlockedConstraintValues( const QgsPoint &point )
1309{
1310 bool previousPointExist, penulPointExist;
1311 const QgsPoint previousPt = previousPointV2( &previousPointExist );
1312 const QgsPoint penultimatePt = penultimatePointV2( &penulPointExist );
1313
1314 // --- angle
1315 if ( !mAngleConstraint->isLocked() && previousPointExist )
1316 {
1317 double prevAngle = 0.0;
1318
1319 if ( penulPointExist && mAngleConstraint->relative() )
1320 {
1321 // previous angle
1322 prevAngle = std::atan2( previousPt.y() - penultimatePt.y(),
1323 previousPt.x() - penultimatePt.x() ) * 180 / M_PI;
1324 }
1325
1326 const double xAngle { std::atan2( point.y() - previousPt.y(),
1327 point.x() - previousPt.x() ) * 180 / M_PI };
1328
1329 // Modulus
1330 const double angle = std::fmod( xAngle - prevAngle, 360.0 );
1331 mAngleConstraint->setValue( angle );
1332
1333 // Bearing (azimuth)
1334 double bearing { std::fmod( xAngle, 360.0 ) };
1335 bearing = bearing <= 90.0 ? 90.0 - bearing : ( bearing > 90 ? 270.0 + 180.0 - bearing : 270.0 - bearing );
1336 const QgsNumericFormatContext context;
1337 const QString bearingText { QgsProject::instance()->displaySettings()->bearingFormat()->formatDouble( bearing, context ) };
1338 emit valueBearingChanged( bearingText );
1339
1340 }
1341 // --- distance
1342 if ( !mDistanceConstraint->isLocked() && previousPointExist )
1343 {
1344 mDistanceConstraint->setValue( std::sqrt( previousPt.distanceSquared( point ) ) );
1345 }
1346 // --- X
1347 if ( !mXConstraint->isLocked() )
1348 {
1349 if ( previousPointExist && mXConstraint->relative() )
1350 {
1351 mXConstraint->setValue( point.x() - previousPt.x() );
1352 }
1353 else
1354 {
1355 mXConstraint->setValue( point.x() );
1356 }
1357 }
1358 // --- Y
1359 if ( !mYConstraint->isLocked() )
1360 {
1361 if ( previousPointExist && mYConstraint->relative() )
1362 {
1363 mYConstraint->setValue( point.y() - previousPt.y() );
1364 }
1365 else
1366 {
1367 mYConstraint->setValue( point.y() );
1368 }
1369 }
1370 // --- Z
1371 if ( !mZConstraint->isLocked() )
1372 {
1373 if ( previousPointExist && mZConstraint->relative() )
1374 {
1375 mZConstraint->setValue( point.z() - previousPt.z() );
1376 }
1377 else
1378 {
1379 mZConstraint->setValue( point.z() );
1380 }
1381 }
1382 // --- M
1383 if ( !mMConstraint->isLocked() )
1384 {
1385 if ( previousPointExist && mMConstraint->relative() )
1386 {
1387 mMConstraint->setValue( point.m() - previousPt.m() );
1388 }
1389 else
1390 {
1391 mMConstraint->setValue( point.m() );
1392 }
1393 }
1394}
1395
1396
1397QList<QgsPointXY> QgsAdvancedDigitizingDockWidget::snapSegmentToAllLayers( const QgsPointXY &originalMapPoint, bool *snapped ) const
1398{
1399 QList<QgsPointXY> segment;
1400 QgsPointXY pt1, pt2;
1402
1403 QgsSnappingUtils *snappingUtils = mMapCanvas->snappingUtils();
1404
1405 const QgsSnappingConfig canvasConfig = snappingUtils->config();
1406 QgsSnappingConfig localConfig = snappingUtils->config();
1407
1410 snappingUtils->setConfig( localConfig );
1411
1412 match = snappingUtils->snapToMap( originalMapPoint, nullptr, true );
1413
1414 snappingUtils->setConfig( canvasConfig );
1415
1416 if ( match.isValid() && match.hasEdge() )
1417 {
1418 match.edgePoints( pt1, pt2 );
1419 segment << pt1 << pt2;
1420 }
1421
1422 if ( snapped )
1423 {
1424 *snapped = segment.count() == 2;
1425 }
1426
1427 return segment;
1428}
1429
1431{
1432 if ( mBetweenLineConstraint == Qgis::BetweenLineConstraint::NoConstraint )
1433 {
1434 return false;
1435 }
1436
1437 bool previousPointExist, penulPointExist, snappedSegmentExist;
1438 const QgsPoint previousPt = previousPointV2( &previousPointExist );
1439 const QgsPoint penultimatePt = penultimatePointV2( &penulPointExist );
1440 mSnappedSegment = snapSegmentToAllLayers( e->originalMapPoint(), &snappedSegmentExist );
1441
1442 if ( !previousPointExist || !snappedSegmentExist )
1443 {
1444 return false;
1445 }
1446
1447 double angle = std::atan2( mSnappedSegment[0].y() - mSnappedSegment[1].y(), mSnappedSegment[0].x() - mSnappedSegment[1].x() );
1448
1449 if ( mAngleConstraint->relative() && penulPointExist )
1450 {
1451 angle -= std::atan2( previousPt.y() - penultimatePt.y(), previousPt.x() - penultimatePt.x() );
1452 }
1453
1454 if ( mBetweenLineConstraint == Qgis::BetweenLineConstraint::Perpendicular )
1455 {
1456 angle += M_PI_2;
1457 }
1458
1459 angle *= 180 / M_PI;
1460
1461 mAngleConstraint->setValue( angle );
1462 mAngleConstraint->setLockMode( lockMode );
1463 if ( lockMode == CadConstraint::HardLock )
1464 {
1465 mBetweenLineConstraint = Qgis::BetweenLineConstraint::NoConstraint;
1466 }
1467
1468 return true;
1469}
1470
1472{
1473 // event on map tool
1474
1475 if ( !mCadEnabled )
1476 return false;
1477
1478 switch ( e->key() )
1479 {
1480 case Qt::Key_Backspace:
1481 case Qt::Key_Delete:
1482 {
1484 releaseLocks( false );
1485 break;
1486 }
1487 case Qt::Key_Escape:
1488 {
1489 releaseLocks();
1490 break;
1491 }
1492 default:
1493 {
1494 keyPressEvent( e );
1495 break;
1496 }
1497 }
1498 // for map tools, continues with key press in any case
1499 return false;
1500}
1501
1507
1509{
1510 // event on dock (this)
1511
1512 if ( !mCadEnabled )
1513 return;
1514
1515 switch ( e->key() )
1516 {
1517 case Qt::Key_Backspace:
1518 case Qt::Key_Delete:
1519 {
1521 releaseLocks( false );
1522 break;
1523 }
1524 case Qt::Key_Escape:
1525 {
1526 releaseLocks();
1527 break;
1528 }
1529 default:
1530 {
1531 filterKeyPress( e );
1532 break;
1533 }
1534 }
1535}
1536
1537void QgsAdvancedDigitizingDockWidget::setPoints( const QList<QgsPointXY> &points )
1538{
1539 clearPoints();
1540 const auto constPoints = points;
1541 for ( const QgsPointXY &pt : constPoints )
1542 {
1543 addPoint( pt );
1544 }
1545}
1546
1547bool QgsAdvancedDigitizingDockWidget::eventFilter( QObject *obj, QEvent *event )
1548{
1549 if ( !cadEnabled() )
1550 {
1551 return QgsDockWidget::eventFilter( obj, event );
1552 }
1553
1554 // event for line edits and map canvas
1555 // we have to catch both KeyPress events and ShortcutOverride events. This is because
1556 // the Ctrl+D and Ctrl+A shortcuts for locking distance/angle clash with the global
1557 // "remove layer" and "select all" shortcuts. Catching ShortcutOverride events allows
1558 // us to intercept these keystrokes before they are caught by the global shortcuts
1559 if ( event->type() == QEvent::ShortcutOverride || event->type() == QEvent::KeyPress )
1560 {
1561 if ( QKeyEvent *keyEvent = dynamic_cast<QKeyEvent *>( event ) )
1562 {
1563 return filterKeyPress( keyEvent );
1564 }
1565 }
1566 return QgsDockWidget::eventFilter( obj, event );
1567}
1568
1569bool QgsAdvancedDigitizingDockWidget::filterKeyPress( QKeyEvent *e )
1570{
1571 // we need to be careful here -- because this method is called on both KeyPress events AND
1572 // ShortcutOverride events, we have to take care that we don't trigger the handling for BOTH
1573 // these event types for a single key press. I.e. pressing "A" may first call trigger a
1574 // ShortcutOverride event (sometimes, not always!) followed immediately by a KeyPress event.
1575 const QEvent::Type type = e->type();
1576 switch ( e->key() )
1577 {
1578 case Qt::Key_X:
1579 {
1580 // modifier+x ONLY caught for ShortcutOverride events...
1581 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1582 {
1583 mXConstraint->toggleLocked();
1584 emit lockXChanged( mXConstraint->isLocked() );
1585 emit pointChangedV2( mCadPointList.value( 0 ) );
1586 e->accept();
1587 }
1588 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1589 {
1590 if ( mCapacities.testFlag( RelativeCoordinates ) )
1591 {
1592 mXConstraint->toggleRelative();
1593 emit relativeXChanged( mXConstraint->relative() );
1594 emit pointChangedV2( mCadPointList.value( 0 ) );
1595 e->accept();
1596 }
1597 }
1598 // .. but "X" alone ONLY caught for KeyPress events (see comment at start of function)
1599 else if ( type == QEvent::KeyPress )
1600 {
1601 mXLineEdit->setFocus();
1602 mXLineEdit->selectAll();
1603 emit focusOnXRequested();
1604 e->accept();
1605 }
1606 break;
1607 }
1608 case Qt::Key_Y:
1609 {
1610 // modifier+y ONLY caught for ShortcutOverride events...
1611 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1612 {
1613 mYConstraint->toggleLocked();
1614 emit lockYChanged( mYConstraint->isLocked() );
1615 emit pointChangedV2( mCadPointList.value( 0 ) );
1616 e->accept();
1617 }
1618 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1619 {
1620 if ( mCapacities.testFlag( RelativeCoordinates ) )
1621 {
1622 mYConstraint->toggleRelative();
1623 emit relativeYChanged( mYConstraint->relative() );
1624 emit pointChangedV2( mCadPointList.value( 0 ) );
1625 e->accept();
1626 }
1627 }
1628 // .. but "y" alone ONLY caught for KeyPress events (see comment at start of function)
1629 else if ( type == QEvent::KeyPress )
1630 {
1631 mYLineEdit->setFocus();
1632 mYLineEdit->selectAll();
1633 emit focusOnYRequested();
1634 e->accept();
1635 }
1636 break;
1637 }
1638 case Qt::Key_Z:
1639 {
1640 // modifier+z ONLY caught for ShortcutOverride events...
1641 if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::AltModifier )
1642 {
1643 mZConstraint->toggleLocked();
1644 emit lockZChanged( mZConstraint->isLocked() );
1645 emit pointChangedV2( mCadPointList.value( 0 ) );
1646 e->accept();
1647 }
1648 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1649 {
1650 if ( mCapacities.testFlag( RelativeCoordinates ) )
1651 {
1652 mZConstraint->toggleRelative();
1653 emit relativeZChanged( mZConstraint->relative() );
1654 emit pointChangedV2( mCadPointList.value( 0 ) );
1655 e->accept();
1656 }
1657 }
1658 // .. but "z" alone ONLY caught for KeyPress events (see comment at start of function)
1659 else if ( type == QEvent::KeyPress )
1660 {
1661 mZLineEdit->setFocus();
1662 mZLineEdit->selectAll();
1663 emit focusOnZRequested();
1664 e->accept();
1665 }
1666 break;
1667 }
1668 case Qt::Key_M:
1669 {
1670 // modifier+m ONLY caught for ShortcutOverride events...
1671 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1672 {
1673 mMConstraint->toggleLocked();
1674 emit lockMChanged( mMConstraint->isLocked() );
1675 emit pointChangedV2( mCadPointList.value( 0 ) );
1676 e->accept();
1677 }
1678 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1679 {
1680 if ( mCapacities.testFlag( RelativeCoordinates ) )
1681 {
1682 mMConstraint->toggleRelative();
1683 emit relativeMChanged( mMConstraint->relative() );
1684 emit pointChangedV2( mCadPointList.value( 0 ) );
1685 e->accept();
1686 }
1687 }
1688 // .. but "m" alone ONLY caught for KeyPress events (see comment at start of function)
1689 else if ( type == QEvent::KeyPress )
1690 {
1691 mMLineEdit->setFocus();
1692 mMLineEdit->selectAll();
1693 emit focusOnMRequested();
1694 e->accept();
1695 }
1696 break;
1697 }
1698 case Qt::Key_A:
1699 {
1700 // modifier+a ONLY caught for ShortcutOverride events...
1701 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1702 {
1703 if ( mCapacities.testFlag( AbsoluteAngle ) )
1704 {
1705 mAngleConstraint->toggleLocked();
1706 emit lockAngleChanged( mAngleConstraint->isLocked() );
1707 emit pointChangedV2( mCadPointList.value( 0 ) );
1708 e->accept();
1709 }
1710 }
1711 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1712 {
1713 if ( mCapacities.testFlag( RelativeAngle ) )
1714 {
1715 mAngleConstraint->toggleRelative();
1716 emit relativeAngleChanged( mAngleConstraint->relative() );
1717 emit pointChangedV2( mCadPointList.value( 0 ) );
1718 e->accept();
1719 }
1720 }
1721 // .. but "a" alone ONLY caught for KeyPress events (see comment at start of function)
1722 else if ( type == QEvent::KeyPress )
1723 {
1724 mAngleLineEdit->setFocus();
1725 mAngleLineEdit->selectAll();
1726 emit focusOnAngleRequested();
1727 e->accept();
1728 }
1729 break;
1730 }
1731 case Qt::Key_D:
1732 {
1733 // modifier+d ONLY caught for ShortcutOverride events...
1734 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1735 {
1736 if ( mCapacities.testFlag( RelativeCoordinates ) && mCapacities.testFlag( Distance ) )
1737 {
1738 mDistanceConstraint->toggleLocked();
1739 emit lockDistanceChanged( mDistanceConstraint->isLocked() );
1740 emit pointChangedV2( mCadPointList.value( 0 ) );
1741 e->accept();
1742 }
1743 }
1744 // .. but "d" alone ONLY caught for KeyPress events (see comment at start of function)
1745 else if ( type == QEvent::KeyPress )
1746 {
1747 mDistanceLineEdit->setFocus();
1748 mDistanceLineEdit->selectAll();
1750 e->accept();
1751 }
1752 break;
1753 }
1754 case Qt::Key_C:
1755 {
1756 if ( type == QEvent::KeyPress )
1757 {
1758 setConstructionMode( !mConstructionMode );
1759 e->accept();
1760 }
1761 break;
1762 }
1763 case Qt::Key_P:
1764 {
1765 if ( type == QEvent::KeyPress )
1766 {
1767 const bool parallel = mParallelAction->isChecked();
1768 const bool perpendicular = mPerpendicularAction->isChecked();
1769
1770 if ( !parallel && !perpendicular )
1771 {
1772 lockBetweenLineConstraint( Qgis::BetweenLineConstraint::Perpendicular );
1773 }
1774 else if ( perpendicular )
1775 {
1776 lockBetweenLineConstraint( Qgis::BetweenLineConstraint::Parallel );
1777 }
1778 else
1779 {
1780 lockBetweenLineConstraint( Qgis::BetweenLineConstraint::NoConstraint );
1781 }
1782 e->accept();
1783
1784 // run a fake map mouse event to update the paint item
1785 emit pointChangedV2( mCadPointList.value( 0 ) );
1786 }
1787 break;
1788 }
1789 case Qt::Key_N:
1790 {
1791 if ( type == QEvent::ShortcutOverride )
1792 {
1793 const QList<double> constActionKeys { mCommonAngleActions.keys() };
1794 const int currentAngleActionIndex { static_cast<int>( constActionKeys .indexOf( mCommonAngleConstraint ) ) };
1795 const QList<QAction *> constActions { mCommonAngleActions.values( ) };
1796 QAction *nextAngleAction;
1797 if ( e->modifiers() == Qt::ShiftModifier )
1798 {
1799 nextAngleAction = currentAngleActionIndex == 0 ? constActions.last() : constActions.at( currentAngleActionIndex - 1 );
1800 }
1801 else
1802 {
1803 nextAngleAction = currentAngleActionIndex == constActions.count() - 1 ? constActions.first() : constActions.at( currentAngleActionIndex + 1 );
1804 }
1805 nextAngleAction->trigger();
1806 e->accept();
1807 }
1808 break;
1809 }
1810 default:
1811 {
1812 return false; // continues
1813 }
1814 }
1815 return e->isAccepted();
1816}
1817
1819{
1820 // most of theses lines can be moved to updateCapacity
1821 connect( mMapCanvas, &QgsMapCanvas::destinationCrsChanged, this, &QgsAdvancedDigitizingDockWidget::enable, Qt::UniqueConnection );
1822 if ( mMapCanvas->mapSettings().destinationCrs().isGeographic() )
1823 {
1824 mAngleLineEdit->setToolTip( tr( "Angle constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1825 mDistanceLineEdit->setToolTip( tr( "Distance constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1826
1827 mLabelX->setText( tr( "Long" ) );
1828 mLabelY->setText( tr( "Lat" ) );
1829
1830 mXConstraint->setPrecision( 8 );
1831 mYConstraint->setPrecision( 8 );
1832 }
1833 else
1834 {
1835 mAngleLineEdit->setToolTip( "<b>" + tr( "Angle" ) + "</b><br>(" + tr( "press a for quick access" ) + ")" );
1836 mAngleLineEdit->setToolTip( QString() );
1837
1838 mDistanceLineEdit->setToolTip( "<b>" + tr( "Distance" ) + "</b><br>(" + tr( "press d for quick access" ) + ")" );
1839
1840 mLabelX->setText( tr( "x" ) );
1841 mLabelY->setText( tr( "y" ) );
1842
1843 mXConstraint->setPrecision( 6 );
1844 mYConstraint->setPrecision( 6 );
1845 }
1846
1847 updateCapacity();
1848
1849 mEnableAction->setEnabled( true );
1850 mErrorLabel->hide();
1851 mCadWidget->show();
1852
1853 mCurrentMapToolSupportsCad = true;
1854
1855 if ( mSessionActive && !isVisible() )
1856 {
1857 show();
1858 }
1859 setCadEnabled( mSessionActive );
1860}
1861
1863{
1865
1866 mEnableAction->setEnabled( false );
1867 mErrorLabel->setText( tr( "Advanced digitizing tools are not enabled for the current map tool" ) );
1868 mErrorLabel->show();
1869 mCadWidget->hide();
1870
1871 mCurrentMapToolSupportsCad = false;
1872
1873 mSnapIndicator->setVisible( false );
1874
1875 setCadEnabled( false );
1876}
1877
1879{
1880 mCadPaintItem->update();
1881}
1882
1884{
1885 if ( !force && ( mLineExtensionConstraint->isLocked() || mXyVertexConstraint->isLocked() ) )
1886 {
1887 return;
1888 }
1889
1890 mLockedSnapVertices.clear();
1891}
1892
1893
1895{
1896 QgsPoint pt = pointXYToPoint( point );
1897 if ( !pointsCount() )
1898 {
1899 mCadPointList << pt;
1900 }
1901 else
1902 {
1903 mCadPointList.insert( 0, pt );
1904 }
1905
1906 updateCapacity();
1908}
1909
1911{
1912 if ( !pointsCount() )
1913 return;
1914
1915 const int i = pointsCount() > 1 ? 1 : 0;
1916 mCadPointList.removeAt( i );
1917 updateCapacity();
1919}
1920
1922{
1923 mCadPointList.clear();
1924 mSnappedSegment.clear();
1925
1926 updateCapacity();
1928}
1929
1931{
1932 if ( !pointsCount() )
1933 {
1934 mCadPointList << point;
1935 updateCapacity();
1936 }
1937 else
1938 {
1939 mCadPointList[0] = point;
1940 }
1942}
1943
1945{
1946 if ( mode == mLockMode )
1947 {
1948 return;
1949 }
1950 mLockMode = mode;
1951 mLockerButton->setChecked( mode == HardLock );
1952 if ( mRepeatingLockButton )
1953 {
1954 if ( mode == HardLock )
1955 {
1956 mRepeatingLockButton->setEnabled( true );
1957 }
1958 else
1959 {
1960 mRepeatingLockButton->setChecked( false );
1961 mRepeatingLockButton->setEnabled( false );
1962 }
1963 }
1964
1965 if ( mode == NoLock )
1966 {
1967 mLineEdit->clear();
1968 }
1969
1970}
1971
1973{
1974 mRepeatingLock = repeating;
1975 if ( mRepeatingLockButton )
1976 mRepeatingLockButton->setChecked( repeating );
1977}
1978
1980{
1981 mRelative = relative;
1982 if ( mRelativeButton )
1983 {
1984 mRelativeButton->setChecked( relative );
1985 }
1986}
1987
1989{
1990 mValue = value;
1991 if ( updateWidget && mLineEdit->isEnabled() )
1992 mLineEdit->setText( displayValue() );
1993}
1994
1996{
1997 switch ( mCadConstraintType )
1998 {
2000 {
2001 return QLocale().toString( mValue, 'f', mPrecision ).append( tr( " °" ) );
2002 }
2005 {
2006 if ( mMapCanvas->mapSettings().destinationCrs().isGeographic() )
2007 {
2008 return QLocale().toString( mValue, 'f', mPrecision ).append( tr( " °" ) );
2009 }
2010 else
2011 {
2012 return QLocale().toString( mValue, 'f', mPrecision );
2013 }
2014 }
2016 {
2018 return QgsDistanceArea::formatDistance( mValue, mPrecision, units, true );
2019 }
2023 default:
2024 break;
2025 }
2026 return QLocale().toString( mValue, 'f', mPrecision );
2027}
2028
2030{
2031 setLockMode( mLockMode == HardLock ? NoLock : HardLock );
2032}
2033
2035{
2036 setRelative( !mRelative );
2037}
2038
2040{
2041 mPrecision = precision;
2042 if ( mLineEdit->isEnabled() )
2043 mLineEdit->setText( displayValue() );
2044}
2045
2050
2052{
2053 mCadConstraintType = constraintType;
2054}
2055
2057{
2058 mMapCanvas = mapCanvas;
2059}
2060
2062{
2063 QString value { text.trimmed() };
2064 switch ( constraintType )
2065 {
2067 {
2068 // Remove distance unit suffix
2069 const QString distanceUnit { QgsUnitTypes::toAbbreviatedString( QgsProject::instance()->distanceUnits() ) };
2070 if ( value.endsWith( distanceUnit ) )
2071 {
2072 value.chop( distanceUnit.length() );
2073 }
2074 break;
2075 }
2077 {
2078 // Remove angle suffix
2079 const QString angleUnit { tr( "°" ) };
2080 if ( value.endsWith( angleUnit ) )
2081 {
2082 value.chop( angleUnit.length() );
2083 }
2084 break;
2085 }
2086 default:
2087 break;
2088 }
2089 return value.trimmed();
2090}
2091
2093{
2094 if ( exist )
2095 *exist = pointsCount() > 0;
2096 if ( pointsCount() > 0 )
2097 return mCadPointList.value( 0 );
2098 else
2099 return QgsPoint();
2100}
2101
2103{
2104 if ( pointsCount() > 0 && layer )
2105 {
2106 QgsPoint res = mCadPointList.value( 0 );
2107 const QgsPointXY layerCoordinates = mMapCanvas->mapSettings().mapToLayerCoordinates( layer, res );
2108 res.setX( layerCoordinates.x() );
2109 res.setY( layerCoordinates.y() );
2110 return res;
2111 }
2112 return QgsPoint();
2113}
2114
2116{
2117 if ( exist )
2118 *exist = pointsCount() > 1;
2119 if ( pointsCount() > 1 )
2120 return mCadPointList.value( 1 );
2121 else
2122 return QgsPoint();
2123}
2124
2126{
2127 if ( exist )
2128 *exist = pointsCount() > 2;
2129 if ( pointsCount() > 2 )
2130 return mCadPointList.value( 2 );
2131 else
2132 return QgsPoint();
2133}
2134
2135QgsPoint QgsAdvancedDigitizingDockWidget::pointXYToPoint( const QgsPointXY &point ) const
2136{
2137 return QgsPoint( point.x(), point.y(), getLineZ(), getLineM() );
2138}
2139
2141{
2142 return mZLineEdit->isEnabled() ? QLocale().toDouble( mZLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
2143}
2144
2146{
2147 return mMLineEdit->isEnabled() ? QLocale().toDouble( mMLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
2148}
@ Segment
On segments.
DistanceUnit
Units of distance.
Definition qgis.h:4358
CadConstraintType
Advanced digitizing constraint type.
Definition qgis.h:3495
@ Distance
Distance value.
@ Generic
Generic value.
@ YCoordinate
Y Coordinate value.
@ XCoordinate
X Coordinate value.
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ Vector
Vector layer.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ Raster
Raster layer.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
@ AllLayers
On all vector layers.
BetweenLineConstraint
Between line constraints which can be enabled.
Definition qgis.h:3469
@ NoConstraint
No additional constraint.
@ Perpendicular
Perpendicular.
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:201
The QgsAdvancedDigitizingCanvasItem class draws the graphical elements of the CAD tools (.
void updatePosition() override
called on changed extent or resize event to update position of the item
The CadConstraint is a class for all basic constraints (angle/distance/x/y).
Qgis::CadConstraintType cadConstraintType() const
Returns the constraint type.
void setPrecision(int precision)
Sets the numeric precision (decimal places) to show in the associated widget.
static QString removeSuffix(const QString &text, Qgis::CadConstraintType constraintType)
Removes unit suffix from the constraint text.
QString displayValue() const
Returns a localized formatted string representation of the value.
void setRepeatingLock(bool repeating)
Sets whether a repeating lock is set for the constraint.
void setCadConstraintType(Qgis::CadConstraintType constraintType)
Sets the constraint type to constraintType.
void setRelative(bool relative)
Set if the constraint should be treated relative.
void setValue(double value, bool updateWidget=true)
Set the value of the constraint.
void setMapCanvas(QgsMapCanvas *mapCanvas)
Sets the map canvas to mapCanvas.
void valueDistanceChanged(const QString &value)
Emitted whenever the distance value changes (either the mouse moved, or the user changed the input).
void lockZChanged(bool locked)
Emitted whenever the Z parameter is locked.
void setEnabledZ(bool enable)
Sets whether Z is enabled.
void setPoints(const QList< QgsPointXY > &points)
Configures list of current CAD points.
void setZ(const QString &value, WidgetSetMode mode)
Set the Z value on the widget.
void setY(const QString &value, WidgetSetMode mode)
Set the Y value on the widget.
bool cadEnabled() const
determines if CAD tools are enabled or if map tools behaves "nomally"
bool applyConstraints(QgsMapMouseEvent *e)
apply the CAD constraints.
void setEnabledM(bool enable)
Sets whether M is enabled.
int pointsCount() const
The number of points in the CAD point helper list.
void addPoint(const QgsPointXY &point)
Adds point to the CAD point list.
void releaseLocks(bool releaseRepeatingLocks=true)
unlock all constraints
void switchZM()
Determines if Z or M will be enabled.
void relativeMChanged(bool relative)
Emitted whenever the M parameter is toggled between absolute and relative.
void lockXChanged(bool locked)
Emitted whenever the X parameter is locked.
void softLockLineExtensionChanged(bool locked)
Emitted whenever the soft line extension parameter is locked.
void focusOnXRequested()
Emitted whenever the X field should get the focus using the shortcuts (X).
void valueYChanged(const QString &value)
Emitted whenever the Y value changes (either the mouse moved, or the user changed the input).
QString formatCommonAngleSnapping(double angle)
Returns the formatted label for common angle snapping option.
void focusOnYRequested()
Emitted whenever the Y field should get the focus using the shortcuts (Y).
double getLineM() const
Convenient method to get the M value from the line edit wiget.
void enabledChangedDistance(bool enabled)
Emitted whenever the distance field is enabled or disabled.
void valueZChanged(const QString &value)
Emitted whenever the Z value changes (either the mouse moved, or the user changed the input).
void clearPoints()
Removes all points from the CAD point list.
QgsPoint previousPointV2(bool *exists=nullptr) const
The previous point.
void lockAngleChanged(bool locked)
Emitted whenever the angle parameter is locked.
void updateCadPaintItem()
Updates canvas item that displays constraints on the ma.
void removePreviousPoint()
Removes previous point in the CAD point list.
QgsPoint penultimatePointV2(bool *exists=nullptr) const
The penultimate point.
void pointChangedV2(const QgsPoint &point)
Sometimes a constraint may change the current point out of a mouse event.
void relativeXChanged(bool relative)
Emitted whenever the X parameter is toggled between absolute and relative.
void focusOnAngleRequested()
Emitted whenever the angle field should get the focus using the shortcuts (A).
WidgetSetMode
Type of interaction to simulate when editing values from external widget.
void focusOnZRequested()
Emitted whenever the Z field should get the focus using the shortcuts (Z).
void focusOnMRequested()
Emitted whenever the M field should get the focus using the shortcuts (M).
void popWarning()
Remove any previously emitted warnings (if any)
void valueXChanged(const QString &value)
Emitted whenever the X value changes (either the mouse moved, or the user changed the input).
double getLineZ() const
Convenient method to get the Z value from the line edit wiget.
void updateCurrentPoint(const QgsPoint &point)
Updates the current point in the CAD point list.
void lockMChanged(bool locked)
Emitted whenever the M parameter is locked.
void cadEnabledChanged(bool enabled)
Signals for external widgets that need to update according to current values.
void lockYChanged(bool locked)
Emitted whenever the Y parameter is locked.
void valueAngleChanged(const QString &value)
Emitted whenever the angle value changes (either the mouse moved, or the user changed the input).
void valueMChanged(const QString &value)
Emitted whenever the M value changes (either the mouse moved, or the user changed the input).
void enable()
Enables the tool (call this when an appropriate map tool is set and in the condition to make use of c...
void relativeZChanged(bool relative)
Emitted whenever the Z parameter is toggled between absolute and relative.
@ RelativeAngle
Also for parallel and perpendicular.
@ RelativeCoordinates
This corresponds to distance and relative coordinates.
bool canvasKeyPressEventFilter(QKeyEvent *e)
Filter key events to e.g.
void setAngle(const QString &value, WidgetSetMode mode)
Set the angle value on the widget.
void enabledChangedAngle(bool enabled)
Emitted whenever the angle field is enabled or disabled.
void enabledChangedZ(bool enabled)
Emitted whenever the Z field is enabled or disabled.
void lockDistanceChanged(bool locked)
Emitted whenever the distance parameter is locked.
void relativeAngleChanged(bool relative)
Emitted whenever the angleX parameter is toggled between absolute and relative.
void softLockXyChanged(bool locked)
Emitted whenever the soft x/y extension parameter is locked.
void valueBearingChanged(const QString &value)
Emitted whenever the bearing value changes.
void enabledChangedM(bool enabled)
Emitted whenever the M field is enabled or disabled.
void setDistance(const QString &value, WidgetSetMode mode)
Set the distance value on the widget.
bool alignToSegment(QgsMapMouseEvent *e, QgsAdvancedDigitizingDockWidget::CadConstraint::LockMode lockMode=QgsAdvancedDigitizingDockWidget::CadConstraint::HardLock)
align to segment for between line constraint.
void setX(const QString &value, WidgetSetMode mode)
Set the X value on the widget.
QgsPoint currentPointV2(bool *exists=nullptr) const
The last point.
QgsAdvancedDigitizingDockWidget(QgsMapCanvas *canvas, QWidget *parent=nullptr)
Create an advanced digitizing dock widget.
void clear()
Clear any cached previous clicks and helper lines.
void focusOnDistanceRequested()
Emitted whenever the distance field should get the focus using the shortcuts (D).
QgsPoint currentPointLayerCoordinates(QgsMapLayer *layer) const
Returns the last CAD point, in a map layer's coordinates.
void valueCommonAngleSnappingChanged(double angle)
Emitted whenever the snapping to common angle option changes, angle = 0 means that the functionality ...
void setM(const QString &value, WidgetSetMode mode)
Set the M value on the widget.
void pushWarning(const QString &message)
Push a warning.
void clearLockedSnapVertices(bool force=true)
Removes all points from the locked snap vertex list.
void relativeYChanged(bool relative)
Emitted whenever the Y parameter is toggled between absolute and relative.
The QgsAdvancedDigitizingFloater class is widget that floats next to the mouse pointer,...
void setItemVisibility(const QgsAdvancedDigitizingFloater::FloaterItem &item, bool visible)
Set whether the floater item should be visible or not.
void setActive(bool active)
Set whether the floater should be active or not.
bool active()
Whether the floater is active or not.
QString formatDouble(double value, const QgsNumericFormatContext &context) const override
Returns a formatted string representation of a numeric double value.
Structure with details of one constraint.
Definition qgscadutils.h:42
bool locked
Whether the constraint is active, i.e. should be considered.
Definition qgscadutils.h:55
double value
Numeric value of the constraint (coordinate/distance in map units or angle in degrees)
Definition qgscadutils.h:59
bool relative
Whether the value is relative to previous value.
Definition qgscadutils.h:57
Defines constraints for the QgsCadUtils::alignMapPoint() method.
QgsCadUtils::AlignMapPointConstraint xyVertexConstraint
QgsCadUtils::AlignMapPointConstraint yConstraint
Constraint for Y coordinate.
QgsCadUtils::AlignMapPointConstraint xConstraint
Constraint for X coordinate.
double mapUnitsPerPixel
Map units/pixel ratio from map canvas.
void setCadPoints(const QList< QgsPoint > &points)
Sets the list of recent CAD points (in map coordinates).
void setLockedSnapVertices(const QQueue< QgsPointLocator::Match > &lockedSnapVertices)
Sets the queue of locked vertices.
QgsCadUtils::AlignMapPointConstraint mConstraint
Constraint for M coordinate.
QgsCadUtils::AlignMapPointConstraint distanceConstraint
Constraint for distance.
bool snappingToFeaturesOverridesCommonAngle
Flag to set snapping to features priority over common angle.
QgsCadUtils::AlignMapPointConstraint zConstraint
Constraint for Z coordinate.
QgsSnappingUtils * snappingUtils
Snapping utils that will be used to snap point to map. Must not be nullptr.
QgsCadUtils::AlignMapPointConstraint commonAngleConstraint
Constraint for soft lock to a common angle.
QgsCadUtils::AlignMapPointConstraint lineExtensionConstraint
QgsCadUtils::AlignMapPointConstraint angleConstraint
Constraint for angle.
Structure returned from alignMapPoint() method.
Definition qgscadutils.h:67
Qgis::LineExtensionSide softLockLineExtension
Definition qgscadutils.h:90
QgsPointXY finalMapPoint
map point aligned according to the constraints
Definition qgscadutils.h:73
bool valid
Whether the combination of constraints is actually valid.
Definition qgscadutils.h:70
QgsPointLocator::Match snapMatch
Snapped point - only valid if actually used for something.
Definition qgscadutils.h:79
double softLockCommonAngle
Angle (in degrees) to which we have soft-locked ourselves (if not set it is -1)
Definition qgscadutils.h:88
static QgsCadUtils::AlignMapPointOutput alignMapPoint(const QgsPointXY &originalMapPoint, const QgsCadUtils::AlignMapPointContext &ctx)
Applies X/Y/angle/distance constraints from the given context to a map point.
static QString formatDistance(double distance, int decimals, Qgis::DistanceUnit unit, bool keepBaseUnit=false)
Returns an distance formatted as a friendly string.
QgsDockWidget subclass with more fine-grained control over how the widget is closed or opened.
Class for parsing and evaluation of expressions (formerly called "search strings").
A event filter for watching for focus events on a parent object.
void focusIn()
Emitted when parent object gains focus.
void focusOut()
Emitted when parent object loses focus.
Map canvas is a class for displaying all GIS data types on a canvas.
QgsSnappingUtils * snappingUtils() const
Returns snapping utility class that is associated with map canvas.
void destinationCrsChanged()
Emitted when map CRS has changed.
QgsMapTool * mapTool()
Returns the currently active tool.
double mapUnitsPerPixel() const
Returns the mapUnitsPerPixel (map units per pixel) for the canvas.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QgsMapLayer * currentLayer()
returns current layer (set by legend widget)
Base class for all map layer types.
Definition qgsmaplayer.h:75
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
QgsPointXY originalMapPoint() const
Returns the original, unmodified map point of the mouse cursor.
void setMapPoint(const QgsPointXY &point)
Set the (snapped) point this event points to in map coordinates.
QgsPointXY snapPoint()
snapPoint will snap the points using the map canvas snapping utils configuration
QgsPointXY mapToLayerCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from output CRS to layer's CRS
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
The QgsMapToolAdvancedDigitizing class is a QgsMapTool which gives event directly in map coordinates ...
static double defaultMValue()
Returns default M value.
static double defaultZValue()
Returns default Z value.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
bool isEditable() const override
Returns true if the layer can be edited.
A context for numeric formats.
A class to represent a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
void setY(double y)
Sets the point's y-coordinate.
Definition qgspoint.h:343
void setX(double x)
Sets the point's x-coordinate.
Definition qgspoint.h:332
double z
Definition qgspoint.h:54
double x
Definition qgspoint.h:52
void setM(double m)
Sets the point's m-value.
Definition qgspoint.h:371
void setZ(double z)
Sets the point's z-coordinate.
Definition qgspoint.h:356
double distanceSquared(double x, double y) const
Returns the Cartesian 2D squared distance between this point a specified x, y coordinate.
Definition qgspoint.h:415
double m
Definition qgspoint.h:55
double y
Definition qgspoint.h:53
const QgsBearingNumericFormat * bearingFormat() const
Returns the project bearing's format, which controls how bearings associated with the project are dis...
Qgis::DistanceUnit distanceUnits
Definition qgsproject.h:124
static QgsProject * instance()
Returns the QgsProject singleton instance.
void snappingConfigChanged(const QgsSnappingConfig &config)
Emitted whenever the configuration for snapping has changed.
QgsSnappingConfig snappingConfig
Definition qgsproject.h:116
QgsProjectDisplaySettings * displaySettings
Definition qgsproject.h:126
bool topologicalEditing
Definition qgsproject.h:123
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
bool setValue(const T &value, const QString &dynamicKeyPart=QString()) const
Set settings value.
A boolean settings entry.
static QgsSettingsTreeNode * sTreeDigitizing
This class is a composition of two QSettings instances:
Definition qgssettings.h:64
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Class that shows snapping marker on map canvas for the current snapping match.
This is a container for configuration of the snapping of the project.
void setTypeFlag(Qgis::SnappingTypes type)
define the type of snapping
void setMode(Qgis::SnappingMode mode)
define the mode of snapping
This class has all the configuration of snapping and can return answers to snapping queries.
QgsSnappingConfig config
QgsPointLocator::Match snapToMap(QPoint point, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Snap to map according to the current configuration.
void setConfig(const QgsSnappingConfig &snappingConfig)
The snapping configuration controls the behavior of this object.
static Q_INVOKABLE QString toAbbreviatedString(Qgis::DistanceUnit unit)
Returns a translated abbreviation representing a distance unit.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE Qgis::WkbType wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
static bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static bool hasM(Qgis::WkbType type)
Tests whether a WKB type contains m values.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
double qgsPermissiveToDouble(QString string, bool &ok)
Converts a string to a double in a permissive way, e.g., allowing for incorrect numbers of digits bet...
Definition qgis.cpp:72
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:5349
QLineF segment(int index, QRectF rect, double radius)
int precision
QgsVectorLayer * layer() const
The vector layer where the snap occurred.
QgsPoint interpolatedPoint(const QgsCoordinateReferenceSystem &destinationCrs=QgsCoordinateReferenceSystem()) const
Convenient method to return a point on an edge with linear interpolation of the Z value.
QgsPointXY point() const
for vertex / edge match coords depending on what class returns it (geom.cache: layer coords,...
bool hasEdge() const
Returns true if the Match is an edge.
void edgePoints(QgsPointXY &pt1, QgsPointXY &pt2) const
Only for a valid edge match - obtain endpoints of the edge.
bool hasLineEndpoint() const
Returns true if the Match is a line endpoint (start or end vertex).
bool hasVertex() const
Returns true if the Match is a vertex.