18#include <QCoreApplication>
23#include "moc_qgsadvanceddigitizingdockwidget.cpp"
46#include <QActionGroup>
57 , mMapCanvas( canvas )
58 , mUserInputWidget( userInputWidget )
60 , mCommonAngleConstraint(
QgsSettings().value( QStringLiteral(
"/Cad/CommonAngle" ), 0.0 ).toDouble() )
66 mAngleConstraint.reset(
new CadConstraint( mAngleLineEdit, mLockAngleButton, mRelativeAngleButton, mRepeatingLockAngleButton ) );
68 mAngleConstraint->setMapCanvas( mMapCanvas );
69 mDistanceConstraint.reset(
new CadConstraint( mDistanceLineEdit, mLockDistanceButton,
nullptr, mRepeatingLockDistanceButton ) );
71 mDistanceConstraint->setMapCanvas( mMapCanvas );
72 mXConstraint.reset(
new CadConstraint( mXLineEdit, mLockXButton, mRelativeXButton, mRepeatingLockXButton ) );
74 mXConstraint->setMapCanvas( mMapCanvas );
75 mYConstraint.reset(
new CadConstraint( mYLineEdit, mLockYButton, mRelativeYButton, mRepeatingLockYButton ) );
77 mYConstraint->setMapCanvas( mMapCanvas );
78 mZConstraint.reset(
new CadConstraint( mZLineEdit, mLockZButton, mRelativeZButton, mRepeatingLockZButton ) );
80 mZConstraint->setMapCanvas( mMapCanvas );
81 mMConstraint.reset(
new CadConstraint( mMLineEdit, mLockMButton, mRelativeMButton, mRepeatingLockMButton ) );
83 mMConstraint->setMapCanvas( mMapCanvas );
85 mLineExtensionConstraint.reset(
new CadConstraint(
new QLineEdit(),
new QToolButton() ) );
86 mXyVertexConstraint.reset(
new CadConstraint(
new QLineEdit(),
new QToolButton() ) );
87 mXyVertexConstraint->setMapCanvas( mMapCanvas );
91 mMapCanvas->installEventFilter(
this );
92 mAngleLineEdit->installEventFilter(
this );
93 mDistanceLineEdit->installEventFilter(
this );
94 mXLineEdit->installEventFilter(
this );
95 mYLineEdit->installEventFilter(
this );
96 mZLineEdit->installEventFilter(
this );
97 mMLineEdit->installEventFilter(
this );
100 connect( mEnableAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::activateCad );
101 connect( mConstructionModeAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::setConstructionMode );
102 connect( mParallelAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
103 connect( mPerpendicularAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
104 connect( mLockAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
105 connect( mLockDistanceButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
106 connect( mLockXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
107 connect( mLockYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
108 connect( mLockZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
109 connect( mLockMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
110 connect( mRelativeAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
111 connect( mRelativeXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
112 connect( mRelativeYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
113 connect( mRelativeZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
114 connect( mRelativeMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
115 connect( mRepeatingLockDistanceButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
116 connect( mRepeatingLockAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
117 connect( mRepeatingLockXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
118 connect( mRepeatingLockYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
119 connect( mRepeatingLockZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
120 connect( mRepeatingLockMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
121 connect( mAngleLineEdit, &QLineEdit::returnPressed,
this, [=]() { lockConstraint(); } );
122 connect( mDistanceLineEdit, &QLineEdit::returnPressed,
this, [=]() { lockConstraint(); } );
123 connect( mXLineEdit, &QLineEdit::returnPressed,
this, [=]() { lockConstraint(); } );
124 connect( mYLineEdit, &QLineEdit::returnPressed,
this, [=]() { lockConstraint(); } );
125 connect( mZLineEdit, &QLineEdit::returnPressed,
this, [=]() { lockConstraint(); } );
126 connect( mMLineEdit, &QLineEdit::returnPressed,
this, [=]() { lockConstraint(); } );
127 connect( mAngleLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
128 connect( mDistanceLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
129 connect( mXLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
130 connect( mYLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
131 connect( mZLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
132 connect( mMLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
138 whileBlocking( mAngleLineEdit )->setText( cleanedInputValue );
144 whileBlocking( mDistanceLineEdit )->setText( cleanedInputValue );
156 mCommonAngleActionsMenu =
new QMenu(
this );
158#ifndef __clang_analyzer__
159 QActionGroup *angleButtonGroup =
new QActionGroup( mCommonAngleActionsMenu );
161 QList<QPair<double, QString>> commonAngles;
162 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 } );
163 for ( QList<double>::const_iterator it = anglesDouble.constBegin(); it != anglesDouble.constEnd(); ++it )
169 QMenu *snappingPriorityMenu =
new QMenu( tr(
"Snapping Priority" ), mCommonAngleActionsMenu );
170 QActionGroup *snappingPriorityActionGroup =
new QActionGroup( snappingPriorityMenu );
171 QAction *featuresAction =
new QAction( tr(
"Prioritize Snapping to Features" ), snappingPriorityActionGroup );
172 featuresAction->setCheckable(
true );
173 QAction *anglesAction =
new QAction( tr(
"Prioritize Snapping to Common Angles" ), snappingPriorityActionGroup );
174 anglesAction->setCheckable(
true );
175 snappingPriorityActionGroup->addAction( featuresAction );
176 snappingPriorityActionGroup->addAction( anglesAction );
177 snappingPriorityMenu->addAction( anglesAction );
178 snappingPriorityMenu->addAction( featuresAction );
179 connect( anglesAction, &QAction::changed,
this, [=] {
180 mSnappingPrioritizeFeatures = featuresAction->isChecked();
181 settingsCadSnappingPriorityPrioritizeFeature->
setValue( featuresAction->isChecked() );
183 featuresAction->setChecked( settingsCadSnappingPriorityPrioritizeFeature->
value() );
184 anglesAction->setChecked( !featuresAction->isChecked() );
185 mCommonAngleActionsMenu->addMenu( snappingPriorityMenu );
188 for ( QList<QPair<double, QString>>::const_iterator it = commonAngles.constBegin(); it != commonAngles.constEnd(); ++it )
190 QAction *action =
new QAction( it->second, mCommonAngleActionsMenu );
191 action->setCheckable(
true );
192 action->setChecked( it->first == mCommonAngleConstraint );
193 mCommonAngleActionsMenu->addAction( action );
195#ifndef __clang_analyzer__
196 angleButtonGroup->addAction( action );
198 mCommonAngleActions.insert( it->first, action );
202 QMenu *constructionSettingsMenu =
new QMenu(
this );
204 mRecordConstructionGuides =
new QAction( tr(
"Record Construction Guides" ), constructionSettingsMenu );
205 mRecordConstructionGuides->setCheckable(
true );
206 mRecordConstructionGuides->setChecked( settingsCadRecordConstructionGuides->
value() );
207 constructionSettingsMenu->addAction( mRecordConstructionGuides );
208 connect( mRecordConstructionGuides, &QAction::triggered,
this, [=]() { settingsCadRecordConstructionGuides->
setValue( mRecordConstructionGuides->isChecked() ); } );
210 mShowConstructionGuides =
new QAction( tr(
"Show Construction Guides" ), constructionSettingsMenu );
211 mShowConstructionGuides->setCheckable(
true );
212 mShowConstructionGuides->setChecked( settingsCadShowConstructionGuides->
value() );
213 constructionSettingsMenu->addAction( mShowConstructionGuides );
214 connect( mShowConstructionGuides, &QAction::triggered,
this, [=]() {
215 settingsCadShowConstructionGuides->
setValue( mShowConstructionGuides->isChecked() );
219 mSnapToConstructionGuides =
new QAction( tr(
"Snap to Visible Construction Guides" ), constructionSettingsMenu );
220 mSnapToConstructionGuides->setCheckable(
true );
221 mSnapToConstructionGuides->setChecked( settingsCadSnapToConstructionGuides->
value() );
222 constructionSettingsMenu->addAction( mSnapToConstructionGuides );
223 connect( mSnapToConstructionGuides, &QAction::triggered,
this, [=]() { settingsCadSnapToConstructionGuides->
setValue( mSnapToConstructionGuides->isChecked() ); } );
225 constructionSettingsMenu->addSeparator();
227 mClearConstructionGuides =
new QAction( tr(
"Clear Construction Guides" ), constructionSettingsMenu );
228 constructionSettingsMenu->addAction( mClearConstructionGuides );
229 connect( mClearConstructionGuides, &QAction::triggered,
this, [=]() {
230 resetConstructionGuides();
234 QToolButton *constructionModeToolButton = qobject_cast<QToolButton *>( mToolbar->widgetForAction( mConstructionModeAction ) );
235 constructionModeToolButton->setPopupMode( QToolButton::MenuButtonPopup );
236 constructionModeToolButton->setMenu( constructionSettingsMenu );
237 constructionModeToolButton->setObjectName( QStringLiteral(
"ConstructionModeButton" ) );
240 QMenu *toolsMenu =
new QMenu(
this );
241 connect( toolsMenu, &QMenu::aboutToShow,
this, [=]() {
244 for (
const QString &name : toolMetadataNames )
247 QAction *toolAction =
new QAction( toolMetadata->
icon(), toolMetadata->
visibleName(), toolsMenu );
248 connect( toolAction, &QAction::triggered,
this, [=]() {
251 toolsMenu->addAction( toolAction );
254 qobject_cast<QToolButton *>( mToolbar->widgetForAction( mToolsAction ) )->setPopupMode( QToolButton::InstantPopup );
255 mToolsAction->setMenu( toolsMenu );
257 qobject_cast<QToolButton *>( mToolbar->widgetForAction( mSettingsAction ) )->setPopupMode( QToolButton::InstantPopup );
258 mSettingsAction->setMenu( mCommonAngleActionsMenu );
259 mSettingsAction->setCheckable(
true );
260 mSettingsAction->setToolTip(
"<b>" + tr(
"Snap to common angles" ) +
"</b><br>(" + tr(
"press n to cycle through the options" ) +
")" );
261 mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
262 connect( mCommonAngleActionsMenu, &QMenu::triggered,
this, &QgsAdvancedDigitizingDockWidget::settingsButtonTriggered );
265 QMenu *constructionMenu =
new QMenu(
this );
267 mLineExtensionAction =
new QAction( tr(
"Line Extension" ), constructionMenu );
268 mLineExtensionAction->setCheckable(
true );
269 constructionMenu->addAction( mLineExtensionAction );
270 connect( mLineExtensionAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
272 mXyVertexAction =
new QAction( tr(
"X/Y Point" ), constructionMenu );
273 mXyVertexAction->setCheckable(
true );
274 constructionMenu->addAction( mXyVertexAction );
275 connect( mXyVertexAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
277 auto constructionToolBar = qobject_cast<QToolButton *>( mToolbar->widgetForAction( mConstructionAction ) );
278 constructionToolBar->setPopupMode( QToolButton::InstantPopup );
279 constructionToolBar->setMenu( constructionMenu );
280 constructionToolBar->setObjectName( QStringLiteral(
"ConstructionButton" ) );
282 mConstructionAction->setMenu( mCommonAngleActionsMenu );
283 mConstructionAction->setCheckable(
true );
284 mConstructionAction->setToolTip( tr(
"Construction Tools" ) );
288 mConstructionModeAction->setToolTip(
"<b>" + tr(
"Construction mode" ) +
"</b><br>(" + tr(
"press c to toggle on/off" ) +
")" );
289 mDistanceLineEdit->setToolTip(
"<b>" + tr(
"Distance" ) +
"</b><br>(" + tr(
"press d for quick access" ) +
")" );
290 mLockDistanceButton->setToolTip(
"<b>" + tr(
"Lock distance" ) +
"</b><br>(" + tr(
"press Ctrl + d for quick access" ) +
")" );
291 mRepeatingLockDistanceButton->setToolTip(
"<b>" + tr(
"Continuously lock distance" ) +
"</b>" );
293 mRelativeAngleButton->setToolTip(
"<b>" + tr(
"Toggles relative angle to previous segment" ) +
"</b><br>(" + tr(
"press Shift + a for quick access" ) +
")" );
294 mAngleLineEdit->setToolTip(
"<b>" + tr(
"Angle" ) +
"</b><br>(" + tr(
"press a for quick access" ) +
")" );
295 mLockAngleButton->setToolTip(
"<b>" + tr(
"Lock angle" ) +
"</b><br>(" + tr(
"press Ctrl + a for quick access" ) +
")" );
296 mRepeatingLockAngleButton->setToolTip(
"<b>" + tr(
"Continuously lock angle" ) +
"</b>" );
298 mRelativeXButton->setToolTip(
"<b>" + tr(
"Toggles relative x to previous node" ) +
"</b><br>(" + tr(
"press Shift + x for quick access" ) +
")" );
299 mXLineEdit->setToolTip(
"<b>" + tr(
"X coordinate" ) +
"</b><br>(" + tr(
"press x for quick access" ) +
")" );
300 mLockXButton->setToolTip(
"<b>" + tr(
"Lock x coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + x for quick access" ) +
")" );
301 mRepeatingLockXButton->setToolTip(
"<b>" + tr(
"Continuously lock x coordinate" ) +
"</b>" );
303 mRelativeYButton->setToolTip(
"<b>" + tr(
"Toggles relative y to previous node" ) +
"</b><br>(" + tr(
"press Shift + y for quick access" ) +
")" );
304 mYLineEdit->setToolTip(
"<b>" + tr(
"Y coordinate" ) +
"</b><br>(" + tr(
"press y for quick access" ) +
")" );
305 mLockYButton->setToolTip(
"<b>" + tr(
"Lock y coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + y for quick access" ) +
")" );
306 mRepeatingLockYButton->setToolTip(
"<b>" + tr(
"Continuously lock y coordinate" ) +
"</b>" );
308 mRelativeZButton->setToolTip(
"<b>" + tr(
"Toggles relative z to previous node" ) +
"</b><br>(" + tr(
"press Shift + z for quick access" ) +
")" );
309 mZLineEdit->setToolTip(
"<b>" + tr(
"Z coordinate" ) +
"</b><br>(" + tr(
"press z for quick access" ) +
")" );
310 mLockZButton->setToolTip(
"<b>" + tr(
"Lock z coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + z for quick access" ) +
")" );
311 mRepeatingLockZButton->setToolTip(
"<b>" + tr(
"Continuously lock z coordinate" ) +
"</b>" );
313 mRelativeMButton->setToolTip(
"<b>" + tr(
"Toggles relative m to previous node" ) +
"</b><br>(" + tr(
"press Shift + m for quick access" ) +
")" );
314 mMLineEdit->setToolTip(
"<b>" + tr(
"M coordinate" ) +
"</b><br>(" + tr(
"press m for quick access" ) +
")" );
315 mLockMButton->setToolTip(
"<b>" + tr(
"Lock m coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + m for quick access" ) +
")" );
316 mRepeatingLockMButton->setToolTip(
"<b>" + tr(
"Continuously lock m coordinate" ) +
"</b>" );
327 mFloaterActionsMenu =
new QMenu(
this );
328 qobject_cast<QToolButton *>( mToolbar->widgetForAction( mFloaterAction ) )->setPopupMode( QToolButton::InstantPopup );
329 mFloaterAction->setMenu( mFloaterActionsMenu );
330 mFloaterAction->setCheckable(
true );
332 mFloaterAction->setChecked( mFloater->
active() );
336 QAction *action =
new QAction( tr(
"Show floater" ), mFloaterActionsMenu );
337 action->setCheckable(
true );
338 action->setChecked( mFloater->
active() );
339 mFloaterActionsMenu->addAction( action );
340 connect( action, &QAction::toggled,
this, [=](
bool checked ) {
342 mFloaterAction->setChecked( checked );
346 mFloaterActionsMenu->addSeparator();
349 QAction *action =
new QAction( tr(
"Show distance" ), mFloaterActionsMenu );
350 action->setCheckable(
true );
351 mFloaterActionsMenu->addAction( action );
352 connect( action, &QAction::toggled,
this, [=](
bool checked ) {
355 const bool isDistanceChecked =
QgsSettings().
value( QStringLiteral(
"/Cad/DistanceShowInFloater" ),
true ).toBool();
356 action->setChecked( isDistanceChecked );
362 QAction *action =
new QAction( tr(
"Show angle" ), mFloaterActionsMenu );
363 action->setCheckable(
true );
364 mFloaterActionsMenu->addAction( action );
365 connect( action, &QAction::toggled,
this, [=](
bool checked ) {
368 const bool isAngleChecked =
QgsSettings().
value( QStringLiteral(
"/Cad/AngleShowInFloater" ),
true ).toBool();
369 action->setChecked( isAngleChecked );
375 QAction *action =
new QAction( tr(
"Show XY coordinates" ), mFloaterActionsMenu );
376 action->setCheckable(
true );
377 mFloaterActionsMenu->addAction( action );
378 connect( action, &QAction::toggled,
this, [=](
bool checked ) {
383 const bool isXCoordinateChecked =
QgsSettings().
value( QStringLiteral(
"/Cad/XCoordinateShowInFloater" ),
true ).toBool();
384 action->setChecked( isXCoordinateChecked );
392 QAction *action =
new QAction( tr(
"Show Z value" ), mFloaterActionsMenu );
393 action->setCheckable(
true );
394 mFloaterActionsMenu->addAction( action );
395 connect( action, &QAction::toggled,
this, [=](
bool checked ) {
398 const bool isZCoordinateChecked =
QgsSettings().
value( QStringLiteral(
"/Cad/ZCoordinateShowInFloater" ),
true ).toBool();
399 action->setChecked( isZCoordinateChecked );
406 QAction *action =
new QAction( tr(
"Show M value" ), mFloaterActionsMenu );
407 action->setCheckable(
true );
408 mFloaterActionsMenu->addAction( action );
409 connect( action, &QAction::toggled,
this, [=](
bool checked ) {
412 const bool isMCoordinateChecked =
QgsSettings().
value( QStringLiteral(
"/Cad/MCoordinateShowInFloater" ),
true ).toBool();
413 action->setChecked( isMCoordinateChecked );
420 QAction *action =
new QAction( tr(
"Show bearing/azimuth" ), mFloaterActionsMenu );
421 action->setCheckable(
true );
422 mFloaterActionsMenu->addAction( action );
423 connect( action, &QAction::toggled,
this, [=](
bool checked ) {
426 const bool isBearingChecked =
QgsSettings().
value( QStringLiteral(
"/Cad/BearingShowInFloater" ),
false ).toBool();
427 action->setChecked( isBearingChecked );
434 QAction *action =
new QAction( tr(
"Show common snapping angle" ), mFloaterActionsMenu );
435 action->setCheckable(
true );
436 mFloaterActionsMenu->addAction( action );
437 connect( action, &QAction::toggled,
this, [=](
bool checked ) {
440 const bool isCommonAngleSnappingChecked =
QgsSettings().
value( QStringLiteral(
"/Cad/CommonAngleSnappingShowInFloater" ),
false ).toBool();
441 action->setChecked( isCommonAngleSnappingChecked );
447 updateCapacity(
true );
451 mConstructionGuidesLayer.reset();
462 mCurrentTool->deleteLater();
469 return tr(
"Do Not Snap to Common Angles" );
471 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 );
476 mXLineEdit->setText( value );
479 emit mXLineEdit->returnPressed();
483 QEvent *e =
new QEvent( QEvent::FocusOut );
484 QCoreApplication::postEvent( mXLineEdit, e );
488 emit mXLineEdit->textEdited( value );
493 mYLineEdit->setText( value );
496 emit mYLineEdit->returnPressed();
500 QEvent *e =
new QEvent( QEvent::FocusOut );
501 QCoreApplication::postEvent( mYLineEdit, e );
505 emit mYLineEdit->textEdited( value );
510 mZLineEdit->setText( value );
513 emit mZLineEdit->returnPressed();
517 QEvent *e =
new QEvent( QEvent::FocusOut );
518 QCoreApplication::postEvent( mZLineEdit, e );
522 emit mZLineEdit->textEdited( value );
527 mMLineEdit->setText( value );
530 emit mMLineEdit->returnPressed();
534 QEvent *e =
new QEvent( QEvent::FocusOut );
535 QCoreApplication::postEvent( mMLineEdit, e );
539 emit mMLineEdit->textEdited( value );
544 mAngleLineEdit->setText( value );
547 emit mAngleLineEdit->returnPressed();
551 emit mAngleLineEdit->textEdited( value );
556 mDistanceLineEdit->setText( value );
559 emit mDistanceLineEdit->returnPressed();
563 QEvent *e =
new QEvent( QEvent::FocusOut );
564 QCoreApplication::postEvent( mDistanceLineEdit, e );
568 emit mDistanceLineEdit->textEdited( value );
573void QgsAdvancedDigitizingDockWidget::setCadEnabled(
bool enabled )
575 mCadEnabled = enabled;
576 mEnableAction->setChecked( enabled );
577 mConstructionModeAction->setEnabled( enabled );
578 mSettingsAction->setEnabled( enabled );
579 mInputWidgets->setEnabled( enabled );
580 mFloaterAction->setEnabled( enabled );
581 mConstructionAction->setEnabled( enabled );
582 mToolsAction->setEnabled( enabled );
587 mLineExtensionAction->setChecked(
false );
588 mXyVertexAction->setChecked(
false );
590 mParallelAction->setEnabled(
false );
591 mPerpendicularAction->setEnabled(
false );
594 mCurrentTool->deleteLater();
601 setConstructionMode(
false );
617 bool enableZ =
false;
618 bool enableM =
false;
622 switch ( layer->type() )
635 QgsMeshLayer *mlayer = qobject_cast<QgsMeshLayer *>( layer );
651 mTargetLayerSupportsM = enableM;
652 mTargetLayerSupportsZ = enableZ;
660 if (
enable && !mTargetLayerSupportsZ )
664 mRelativeZButton->setEnabled(
enable );
665 mZLabel->setEnabled(
enable );
666 mZLineEdit->setEnabled(
enable );
667 if ( mZLineEdit->isEnabled() )
671 mLockZButton->setEnabled(
enable );
677 if (
enable && !mTargetLayerSupportsM )
681 mRelativeMButton->setEnabled(
enable );
682 mMLabel->setEnabled(
enable );
683 mMLineEdit->setEnabled(
enable );
684 if ( mMLineEdit->isEnabled() )
688 mLockMButton->setEnabled(
enable );
692void QgsAdvancedDigitizingDockWidget::activateCad(
bool enabled )
694 enabled &= mCurrentMapToolSupportsCad;
696 mSessionActive = enabled;
698 if ( enabled && !isVisible() )
703 setCadEnabled( enabled );
710 mCurrentTool->deleteLater();
711 mCurrentTool =
nullptr;
718 if ( QWidget *toolWidget = mCurrentTool->createWidget() )
720 toolWidget->setParent( mUserInputWidget );
729 return mCurrentTool.data();
732void QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked(
bool activated )
738 else if ( sender() == mParallelAction )
742 else if ( sender() == mPerpendicularAction )
748void QgsAdvancedDigitizingDockWidget::setConstraintRelative(
bool activate )
750 if ( sender() == mRelativeAngleButton )
752 mAngleConstraint->setRelative( activate );
755 else if ( sender() == mRelativeXButton )
757 mXConstraint->setRelative( activate );
760 else if ( sender() == mRelativeYButton )
762 mYConstraint->setRelative( activate );
765 else if ( sender() == mRelativeZButton )
767 mZConstraint->setRelative( activate );
770 else if ( sender() == mRelativeMButton )
772 mMConstraint->setRelative( activate );
777void QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock(
bool activate )
779 if ( sender() == mRepeatingLockDistanceButton )
781 mDistanceConstraint->setRepeatingLock( activate );
783 else if ( sender() == mRepeatingLockAngleButton )
785 mAngleConstraint->setRepeatingLock( activate );
787 else if ( sender() == mRepeatingLockXButton )
789 mXConstraint->setRepeatingLock( activate );
791 else if ( sender() == mRepeatingLockYButton )
793 mYConstraint->setRepeatingLock( activate );
795 else if ( sender() == mRepeatingLockZButton )
797 mZConstraint->setRepeatingLock( activate );
799 else if ( sender() == mRepeatingLockMButton )
801 mMConstraint->setRepeatingLock( activate );
805void QgsAdvancedDigitizingDockWidget::setConstructionMode(
bool enabled )
807 mConstructionMode = enabled;
808 mConstructionModeAction->setChecked( enabled );
812 if ( enabled && mCadPointList.size() > 1 )
814 mConstructionGuideLine.
addVertex( mCadPointList.at( 1 ) );
819void QgsAdvancedDigitizingDockWidget::settingsButtonTriggered( QAction *action )
822 for (
auto it = mCommonAngleActions.cbegin(); it != mCommonAngleActions.cend(); ++it )
824 if ( it.value() == action )
826 it.value()->setChecked(
true );
827 mCommonAngleConstraint = it.key();
829 mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
836QgsMapLayer *QgsAdvancedDigitizingDockWidget::targetLayer()
const
840 return advancedTool->layer();
854 if ( releaseRepeatingLocks )
856 mXyVertexAction->setChecked(
false );
860 mLineExtensionAction->setChecked(
false );
867 if ( releaseRepeatingLocks || !mAngleConstraint->isRepeatingLock() )
872 if ( releaseRepeatingLocks || !mDistanceConstraint->isRepeatingLock() )
877 if ( releaseRepeatingLocks || !mXConstraint->isRepeatingLock() )
882 if ( releaseRepeatingLocks || !mYConstraint->isRepeatingLock() )
887 if ( releaseRepeatingLocks || !mZConstraint->isRepeatingLock() )
892 if ( releaseRepeatingLocks || !mMConstraint->isRepeatingLock() )
898 if ( !mCadPointList.empty() )
900 if ( !mXConstraint->isLocked() && !mXConstraint->relative() )
902 mXConstraint->setValue( mCadPointList.constLast().x(),
true );
904 if ( !mYConstraint->isLocked() && !mYConstraint->relative() )
906 mYConstraint->setValue( mCadPointList.constLast().y(),
true );
908 if ( !mZConstraint->isLocked() && !mZConstraint->relative() )
910 mZConstraint->setValue( mCadPointList.constLast().z(),
true );
912 if ( !mMConstraint->isLocked() && !mMConstraint->relative() )
914 mMConstraint->setValue( mCadPointList.constLast().m(),
true );
920void QgsAdvancedDigitizingDockWidget::emit pointChanged()
923 QPoint globalPos = mMapCanvas->cursor().pos();
924 QPoint pos = mMapCanvas->mapFromGlobal( globalPos );
925 QMouseEvent *e =
new QMouseEvent( QEvent::MouseMove, pos, globalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier );
926 mCurrentMapTool->canvasMoveEvent( e );
932 CadConstraint *constraint =
nullptr;
933 if ( obj == mAngleLineEdit || obj == mLockAngleButton )
935 constraint = mAngleConstraint.get();
937 else if ( obj == mDistanceLineEdit || obj == mLockDistanceButton )
939 constraint = mDistanceConstraint.get();
941 else if ( obj == mXLineEdit || obj == mLockXButton )
943 constraint = mXConstraint.get();
945 else if ( obj == mYLineEdit || obj == mLockYButton )
947 constraint = mYConstraint.get();
949 else if ( obj == mZLineEdit || obj == mLockZButton )
951 constraint = mZConstraint.get();
953 else if ( obj == mMLineEdit || obj == mLockMButton )
955 constraint = mMConstraint.get();
957 else if ( obj == mLineExtensionAction )
959 constraint = mLineExtensionConstraint.get();
961 else if ( obj == mXyVertexAction )
963 constraint = mXyVertexConstraint.get();
968double QgsAdvancedDigitizingDockWidget::parseUserInput(
const QString &inputValue,
const Qgis::CadConstraintType type,
bool &ok )
const
979 const QVariant result = expr.evaluate();
980 if ( expr.hasEvalError() )
983 QString inputValueC { inputValue };
986 if ( inputValue.contains( QLocale().groupSeparator() ) )
988 inputValueC.remove( QLocale().groupSeparator() );
990 const QVariant resultC = exprC.evaluate();
991 if ( !exprC.hasEvalError() )
993 value = resultC.toDouble( &ok );
998 if ( !ok && QLocale().decimalPoint() != QChar(
'.' ) && inputValueC.contains( QLocale().decimalPoint() ) )
1000 QgsExpression exprC( inputValueC.replace( QLocale().decimalPoint(), QChar(
'.' ) ) );
1001 const QVariant resultC = exprC.evaluate();
1002 if ( !exprC.hasEvalError() )
1004 value = resultC.toDouble( &ok );
1010 value = result.toDouble( &ok );
1025void QgsAdvancedDigitizingDockWidget::updateConstraintValue( CadConstraint *constraint,
const QString &textValue,
bool convertExpression )
1027 if ( !constraint || textValue.isEmpty() )
1036 const double value = parseUserInput( textValue, constraint->cadConstraintType(), ok );
1040 constraint->setValue( value, convertExpression );
1045void QgsAdvancedDigitizingDockWidget::lockConstraint(
bool activate )
1047 CadConstraint *constraint = objectToConstraint( sender() );
1055 const QString textValue = constraint->lineEdit()->text();
1056 if ( !textValue.isEmpty() )
1059 const double value = parseUserInput( textValue, constraint->cadConstraintType(), ok );
1062 constraint->setValue( value );
1076 if ( constraint == mXConstraint.get() )
1080 else if ( constraint == mYConstraint.get() )
1084 else if ( constraint == mZConstraint.get() )
1088 else if ( constraint == mMConstraint.get() )
1092 else if ( constraint == mDistanceConstraint.get() )
1096 else if ( constraint == mAngleConstraint.get() )
1104 if ( constraint == mAngleConstraint.get() )
1114void QgsAdvancedDigitizingDockWidget::constraintTextEdited(
const QString &textValue )
1116 CadConstraint *constraint = objectToConstraint( sender() );
1122 updateConstraintValue( constraint, textValue,
false );
1125void QgsAdvancedDigitizingDockWidget::constraintFocusOut()
1127 QLineEdit *lineEdit = qobject_cast<QLineEdit *>( sender()->parent() );
1131 CadConstraint *constraint = objectToConstraint( lineEdit );
1137 updateConstraintValue( constraint, lineEdit->text(),
true );
1142 mBetweenLineConstraint = constraint;
1147void QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint(
bool activate )
1149 CadConstraint *constraint = objectToConstraint( sender() );
1157 if ( constraint == mXyVertexConstraint.get() )
1161 else if ( constraint == mLineExtensionConstraint.get() )
1175void QgsAdvancedDigitizingDockWidget::updateCapacity(
bool updateUIwithoutChange )
1181 if ( mCadPointList.count() > 1 )
1184 if ( !isGeographic )
1190 if ( mCadPointList.count() > 2 )
1192 if ( !isGeographic )
1195 if ( !updateUIwithoutChange && newCapacities == mCapacities )
1205 const bool distance = mCadEnabled && newCapacities.testFlag(
Distance );
1206 const bool relativeAngle = mCadEnabled && newCapacities.testFlag(
RelativeAngle );
1207 const bool absoluteAngle = mCadEnabled && newCapacities.testFlag(
AbsoluteAngle );
1208 const bool relativeCoordinates = mCadEnabled && newCapacities.testFlag(
RelativeCoordinates );
1210 mPerpendicularAction->setEnabled( distance && snappingEnabled );
1211 mParallelAction->setEnabled( distance && snappingEnabled );
1213 mLineExtensionAction->setEnabled( snappingEnabled );
1214 mXyVertexAction->setEnabled( snappingEnabled );
1218 if ( !snappingEnabled )
1220 mPerpendicularAction->setToolTip( tr(
"Snapping must be enabled to utilize perpendicular mode." ) );
1221 mParallelAction->setToolTip( tr(
"Snapping must be enabled to utilize parallel mode." ) );
1222 mLineExtensionAction->setToolTip( tr(
"Snapping must be enabled to utilize line extension mode." ) );
1223 mXyVertexAction->setToolTip( tr(
"Snapping must be enabled to utilize xy point mode." ) );
1225 else if ( mCadPointList.count() <= 1 )
1227 mPerpendicularAction->setToolTip( tr(
"A first vertex should be drawn to utilize perpendicular mode." ) );
1228 mParallelAction->setToolTip( tr(
"A first vertex should be drawn to utilize parallel mode." ) );
1230 else if ( isGeographic )
1232 mPerpendicularAction->setToolTip( tr(
"Perpendicular mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1233 mParallelAction->setToolTip( tr(
"Parallel mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1237 mPerpendicularAction->setToolTip(
"<b>" + tr(
"Perpendicular" ) +
"</b><br>(" + tr(
"press p to switch between perpendicular, parallel and normal mode" ) +
")" );
1238 mParallelAction->setToolTip(
"<b>" + tr(
"Parallel" ) +
"</b><br>(" + tr(
"press p to switch between perpendicular, parallel and normal mode" ) +
")" );
1242 if ( !absoluteAngle )
1248 mLockAngleButton->setEnabled( absoluteAngle );
1249 mRelativeAngleButton->setEnabled( relativeAngle );
1250 mAngleLineEdit->setEnabled( absoluteAngle );
1252 if ( !absoluteAngle )
1256 if ( !relativeAngle )
1258 mAngleConstraint->setRelative(
false );
1261 else if ( relativeAngle && !mCapacities.testFlag(
RelativeAngle ) )
1264 mAngleConstraint->setRelative(
true );
1269 mLockDistanceButton->setEnabled( distance && relativeCoordinates );
1270 mDistanceLineEdit->setEnabled( distance && relativeCoordinates );
1272 if ( !( distance && relativeCoordinates ) )
1277 mRelativeXButton->setEnabled( relativeCoordinates );
1278 mRelativeYButton->setEnabled( relativeCoordinates );
1279 mRelativeZButton->setEnabled( relativeCoordinates );
1280 mRelativeMButton->setEnabled( relativeCoordinates );
1283 mCapacities = newCapacities;
1291 constr.
locked =
c->isLocked();
1293 constr.
value =
c->value();
1300 if ( !mLineExtensionConstraint->isLocked() && !mXyVertexConstraint->isLocked() )
1306 const int lastIndex = mLockedSnapVertices.length() - 1;
1307 for (
int i = lastIndex; i >= 0; --i )
1309 if ( mLockedSnapVertices[i].point() == snapMatch.
point() )
1311 if ( snapMatch.
point() != previouslySnap.
point() )
1313 mLockedSnapVertices.removeAt( i );
1319 if ( snapMatch.
point() != previouslySnap.
point() )
1321 mLockedSnapVertices.enqueue( snapMatch );
1324 if ( mLockedSnapVertices.count() > 3 )
1326 mLockedSnapVertices.dequeue();
1335 context.
xConstraint = _constraint( mXConstraint.get() );
1336 context.
yConstraint = _constraint( mYConstraint.get() );
1337 context.
zConstraint = _constraint( mZConstraint.get() );
1338 context.
mConstraint = _constraint( mMConstraint.get() );
1365 const bool res = output.
valid;
1367 mSnappedSegment.clear();
1372 mSnappedSegment << edgePt0 << edgePt1;
1393 mSnapIndicator->setMatch( output.
snapMatch );
1394 mSnapIndicator->setVisible(
true );
1398 mSnapIndicator->setVisible(
false );
1420 if ( mSnapMatch.
layer() )
1436 toggleLockedSnapVertex( mSnapMatch, mLastSnapMatch );
1437 mLastSnapMatch = mSnapMatch;
1447 if ( mLockZButton->isChecked() )
1449 point.
setZ( QLocale().toDouble( mZLineEdit->text() ) );
1451 if ( mLockMButton->isChecked() )
1453 point.
setM( QLocale().toDouble( mMLineEdit->text() ) );
1459 updateUnlockedConstraintValues( point );
1467 emit
pushWarning( tr(
"Some constraints are incompatible. Resulting point might be incorrect." ) );
1474void QgsAdvancedDigitizingDockWidget::updateUnlockedConstraintValues(
const QgsPoint &point )
1476 bool previousPointExist, penulPointExist;
1481 if ( previousPointExist )
1483 double prevAngle = 0.0;
1485 if ( penulPointExist && mAngleConstraint->relative() )
1488 prevAngle = std::atan2( previousPt.
y() - penultimatePt.
y(), previousPt.
x() - penultimatePt.
x() ) * 180 / M_PI;
1491 const double xAngle { std::atan2( point.
y() - previousPt.
y(), point.
x() - previousPt.
x() ) * 180 / M_PI };
1494 const double angle = std::fmod( xAngle - prevAngle, 360.0 );
1495 if ( !mAngleConstraint->isLocked() )
1497 mAngleConstraint->setValue( angle );
1501 double bearing { std::fmod( xAngle, 360.0 ) };
1502 bearing = bearing <= 90.0 ? 90.0 - bearing : ( bearing > 90 ? 270.0 + 180.0 - bearing : 270.0 - bearing );
1508 if ( !mDistanceConstraint->isLocked() && previousPointExist )
1510 mDistanceConstraint->setValue( std::sqrt( previousPt.
distanceSquared( point ) ) );
1513 if ( !mXConstraint->isLocked() )
1515 if ( previousPointExist && mXConstraint->relative() )
1517 mXConstraint->setValue( point.
x() - previousPt.
x() );
1521 mXConstraint->setValue( point.
x() );
1525 if ( !mYConstraint->isLocked() )
1527 if ( previousPointExist && mYConstraint->relative() )
1529 mYConstraint->setValue( point.
y() - previousPt.
y() );
1533 mYConstraint->setValue( point.
y() );
1537 if ( !mZConstraint->isLocked() )
1539 if ( previousPointExist && mZConstraint->relative() )
1541 mZConstraint->setValue( point.
z() - previousPt.
z() );
1545 mZConstraint->setValue( point.
z() );
1549 if ( !mMConstraint->isLocked() )
1551 if ( previousPointExist && mMConstraint->relative() )
1553 mMConstraint->setValue( point.
m() - previousPt.
m() );
1557 mMConstraint->setValue( point.
m() );
1563QList<QgsPointXY> QgsAdvancedDigitizingDockWidget::snapSegmentToAllLayers(
const QgsPointXY &originalMapPoint,
bool *snapped )
const
1576 snappingUtils->
setConfig( localConfig );
1578 match = snappingUtils->
snapToMap( originalMapPoint,
nullptr,
true );
1580 snappingUtils->
setConfig( canvasConfig );
1590 *snapped =
segment.count() == 2;
1600 mCurrentTool->canvasPressEvent( event );
1605 event->setAccepted(
false );
1617 mCurrentTool->canvasMoveEvent( event );
1625 if ( event->button() == Qt::RightButton )
1629 mCurrentTool->canvasReleaseEvent( event );
1630 if ( !event->isAccepted() )
1642 event->setAccepted(
false );
1648 mCurrentTool->canvasReleaseEvent( event );
1649 if ( !event->isAccepted() )
1660 if ( mLockZButton->isChecked() )
1662 point.
setZ( QLocale().toDouble( mZLineEdit->text() ) );
1664 if ( mLockMButton->isChecked() )
1666 point.
setM( QLocale().toDouble( mMLineEdit->text() ) );
1677 event->setAccepted(
false );
1689 bool previousPointExist, penulPointExist, snappedSegmentExist;
1692 mSnappedSegment = snapSegmentToAllLayers( e->
originalMapPoint(), &snappedSegmentExist );
1694 if ( !previousPointExist || !snappedSegmentExist )
1699 double angle = std::atan2( mSnappedSegment[0].y() - mSnappedSegment[1].y(), mSnappedSegment[0].x() - mSnappedSegment[1].x() );
1701 if ( mAngleConstraint->relative() && penulPointExist )
1703 angle -= std::atan2( previousPt.
y() - penultimatePt.
y(), previousPt.
x() - penultimatePt.
x() );
1711 angle *= 180 / M_PI;
1713 mAngleConstraint->setValue( angle );
1714 mAngleConstraint->setLockMode( lockMode );
1732 case Qt::Key_Backspace:
1733 case Qt::Key_Delete:
1739 case Qt::Key_Escape:
1758 mCurrentTool->deleteLater();
1761 if ( !mConstructionGuideLine.
isEmpty() )
1763 mConstructionGuideLine.
clear();
1779 case Qt::Key_Backspace:
1780 case Qt::Key_Delete:
1786 case Qt::Key_Escape:
1790 if ( mConstructionGuideLine.
numPoints() >= 2 )
1792 mConstructionGuidesLayer->dataProvider()->deleteFeatures(
QgsFeatureIds() << mConstructionGuideId );
1793 mConstructionGuideLine.
clear();
1798 mCurrentTool->deleteLater();
1805 filterKeyPress( e );
1814 const auto constPoints = points;
1823 mDistanceConstraint->toggleLocked();
1828bool QgsAdvancedDigitizingDockWidget::eventFilter( QObject *obj, QEvent *event )
1832 return QgsDockWidget::eventFilter( obj, event );
1840 if ( event->type() == QEvent::ShortcutOverride || event->type() == QEvent::KeyPress )
1842 if ( QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( event ) )
1844 return filterKeyPress( keyEvent );
1847 return QgsDockWidget::eventFilter( obj, event );
1850bool QgsAdvancedDigitizingDockWidget::filterKeyPress( QKeyEvent *e )
1856 const QEvent::Type type = e->type();
1859 case Qt::Key_Escape:
1861 if ( type == QEvent::KeyPress && mCurrentTool )
1863 mCurrentTool->deleteLater();
1865 else if ( type == QEvent::KeyPress && mConstructionMode && mConstructionGuideLine.
numPoints() >= 2 )
1867 mConstructionGuidesLayer->dataProvider()->deleteFeatures(
QgsFeatureIds() << mConstructionGuideId );
1868 mConstructionGuideLine.
clear();
1870 if ( mCadPointList.size() > 1 )
1872 mConstructionGuideLine.
addVertex( mCadPointList.at( 1 ) );
1887 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1889 mXConstraint->toggleLocked();
1894 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1898 mXConstraint->toggleRelative();
1905 else if ( type == QEvent::KeyPress )
1907 mXLineEdit->setFocus();
1908 mXLineEdit->selectAll();
1917 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1919 mYConstraint->toggleLocked();
1924 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1928 mYConstraint->toggleRelative();
1935 else if ( type == QEvent::KeyPress )
1937 mYLineEdit->setFocus();
1938 mYLineEdit->selectAll();
1947 if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::AltModifier )
1949 mZConstraint->toggleLocked();
1954 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1958 mZConstraint->toggleRelative();
1965 else if ( type == QEvent::KeyPress )
1967 mZLineEdit->setFocus();
1968 mZLineEdit->selectAll();
1977 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1979 mMConstraint->toggleLocked();
1984 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1988 mMConstraint->toggleRelative();
1995 else if ( type == QEvent::KeyPress )
1997 mMLineEdit->setFocus();
1998 mMLineEdit->selectAll();
2007 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
2011 mAngleConstraint->toggleLocked();
2017 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
2021 mAngleConstraint->toggleRelative();
2028 else if ( type == QEvent::KeyPress )
2030 mAngleLineEdit->setFocus();
2031 mAngleLineEdit->selectAll();
2040 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
2049 else if ( type == QEvent::KeyPress )
2051 mDistanceLineEdit->setFocus();
2052 mDistanceLineEdit->selectAll();
2060 if ( type == QEvent::KeyPress )
2062 setConstructionMode( !mConstructionMode );
2069 if ( type == QEvent::KeyPress )
2071 const bool parallel = mParallelAction->isChecked();
2072 const bool perpendicular = mPerpendicularAction->isChecked();
2074 if ( !parallel && !perpendicular )
2078 else if ( perpendicular )
2095 if ( type == QEvent::ShortcutOverride )
2097 const QList<double> constActionKeys { mCommonAngleActions.keys() };
2098 const int currentAngleActionIndex {
static_cast<int>( constActionKeys.indexOf( mCommonAngleConstraint ) ) };
2099 const QList<QAction *> constActions { mCommonAngleActions.values() };
2100 QAction *nextAngleAction;
2101 if ( e->modifiers() == Qt::ShiftModifier )
2103 nextAngleAction = currentAngleActionIndex == 0 ? constActions.last() : constActions.at( currentAngleActionIndex - 1 );
2107 nextAngleAction = currentAngleActionIndex == constActions.count() - 1 ? constActions.first() : constActions.at( currentAngleActionIndex + 1 );
2109 nextAngleAction->trigger();
2119 return e->isAccepted();
2128 mAngleLineEdit->setToolTip( tr(
"Angle constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
2129 mDistanceLineEdit->setToolTip( tr(
"Distance constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
2131 mLabelX->setText( tr(
"Long" ) );
2132 mLabelY->setText( tr(
"Lat" ) );
2134 mXConstraint->setPrecision( 8 );
2135 mYConstraint->setPrecision( 8 );
2139 mAngleLineEdit->setToolTip(
"<b>" + tr(
"Angle" ) +
"</b><br>(" + tr(
"press a for quick access" ) +
")" );
2140 mAngleLineEdit->setToolTip( QString() );
2142 mDistanceLineEdit->setToolTip(
"<b>" + tr(
"Distance" ) +
"</b><br>(" + tr(
"press d for quick access" ) +
")" );
2144 mLabelX->setText( tr(
"x" ) );
2145 mLabelY->setText( tr(
"y" ) );
2147 mXConstraint->setPrecision( 6 );
2148 mYConstraint->setPrecision( 6 );
2153 mEnableAction->setEnabled(
true );
2154 mErrorLabel->hide();
2157 mCurrentMapToolSupportsCad =
true;
2159 if ( mSessionActive && !isVisible() )
2164 setCadEnabled( mSessionActive );
2166 if ( !mConstructionGuidesLayer )
2168 resetConstructionGuides();
2171 if ( mDeferredUpdateConstructionGuidesCrs )
2173 updateConstructionGuidesCrs();
2183 mEnableAction->setEnabled(
false );
2184 mErrorLabel->setText( tr(
"Advanced digitizing tools are not enabled for the current map tool" ) );
2185 mErrorLabel->show();
2188 mCurrentMapToolSupportsCad =
false;
2190 mSnapIndicator->setVisible(
false );
2192 setCadEnabled(
false );
2197 mCadPaintItem->update();
2202 if ( !force && ( mLineExtensionConstraint->isLocked() || mXyVertexConstraint->isLocked() ) )
2207 mLockedSnapVertices.clear();
2212 QgsPoint pt = pointXYToPoint( point );
2215 mCadPointList << pt;
2219 mCadPointList.insert( 0, pt );
2228 if ( mConstructionGuideLine.
numPoints() == 2 )
2233 mConstructionGuidesLayer->dataProvider()->addFeature( feature );
2234 mConstructionGuideId = feature.
id();
2236 else if ( mConstructionGuideLine.
numPoints() > 2 )
2239 mConstructionGuidesLayer->dataProvider()->changeGeometryValues( { { mConstructionGuideId, geom } } );
2244 if ( !mConstructionGuideLine.
isEmpty() )
2249 mConstructionGuidesLayer->dataProvider()->changeGeometryValues( { { mConstructionGuideId, geom } } );
2250 mConstructionGuideLine.
clear();
2265 mCadPointList.removeAt( i );
2272 mCadPointList.clear();
2273 mSnappedSegment.clear();
2283 mCadPointList << point;
2288 mCadPointList[0] = point;
2295 if ( mode == mLockMode )
2300 mLockerButton->setChecked( mode ==
HardLock );
2301 if ( mRepeatingLockButton )
2305 mRepeatingLockButton->setEnabled(
true );
2309 mRepeatingLockButton->setChecked(
false );
2310 mRepeatingLockButton->setEnabled(
false );
2322 mRepeatingLock = repeating;
2323 if ( mRepeatingLockButton )
2324 mRepeatingLockButton->setChecked( repeating );
2329 mRelative = relative;
2330 if ( mRelativeButton )
2332 mRelativeButton->setChecked( relative );
2339 if ( updateWidget && mLineEdit->isEnabled() )
2340 mLineEdit->setText( displayValue() );
2345 switch ( mCadConstraintType )
2349 return QLocale().toString( mValue,
'f', mPrecision ).append( tr(
" °" ) );
2356 return QLocale().toString( mValue,
'f', mPrecision ).append( tr(
" °" ) );
2360 return QLocale().toString( mValue,
'f', mPrecision );
2377 return QLocale().toString( mValue,
'f', mPrecision );
2382 setLockMode( mLockMode == HardLock ? NoLock : HardLock );
2387 setRelative( !mRelative );
2393 if ( mLineEdit->isEnabled() )
2394 mLineEdit->setText( displayValue() );
2399 return mCadConstraintType;
2404 mCadConstraintType = constraintType;
2409 mMapCanvas = mapCanvas;
2414 QString value { text.trimmed() };
2415 switch ( constraintType )
2421 if ( value.endsWith( distanceUnit ) )
2423 value.chop( distanceUnit.length() );
2430 const QString angleUnit { tr(
"°" ) };
2431 if ( value.endsWith( angleUnit ) )
2433 value.chop( angleUnit.length() );
2440 return value.trimmed();
2448 return mCadPointList.value( 0 );
2457 QgsPoint res = mCadPointList.value( 0 );
2459 res.
setX( layerCoordinates.
x() );
2460 res.
setY( layerCoordinates.
y() );
2471 return mCadPointList.value( 1 );
2481 return mCadPointList.value( 2 );
2486QgsPoint QgsAdvancedDigitizingDockWidget::pointXYToPoint(
const QgsPointXY &point )
const
2493 return mZLineEdit->isEnabled() ? QLocale().toDouble( mZLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
2498 return mMLineEdit->isEnabled() ? QLocale().toDouble( mMLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
2503 return mShowConstructionGuides ? mShowConstructionGuides->isChecked() :
false;
2508 return mSnapToConstructionGuides ? mShowConstructionGuides->isChecked() && mSnapToConstructionGuides->isChecked() :
false;
2513 return mRecordConstructionGuides ? mRecordConstructionGuides->isChecked() :
false;
2516void QgsAdvancedDigitizingDockWidget::updateConstructionGuidesCrs()
2518 if ( !mConstructionGuidesLayer )
2525 mDeferredUpdateConstructionGuidesCrs =
true;
2536 mConstructionGuidesLayer->dataProvider()->changeGeometryValues( { { feature.
id(), geom } } );
2539 mDeferredUpdateConstructionGuidesCrs =
false;
2542void QgsAdvancedDigitizingDockWidget::resetConstructionGuides()
2544 if ( mConstructionGuidesLayer )
2546 mConstructionGuidesLayer.reset();
2550 mConstructionGuidesLayer = std::make_unique<QgsVectorLayer>( QStringLiteral(
"LineString?crs=%1" ).arg( mMapCanvas->
mapSettings().
destinationCrs().
authid() ), QStringLiteral(
"constructionGuides" ), QStringLiteral(
"memory" ), options );
DistanceUnit
Units of distance.
CadConstraintType
Advanced digitizing constraint type.
@ Distance
Distance 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.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
@ AllLayers
On all vector layers.
BetweenLineConstraint
Between line constraints which can be enabled.
@ NoConstraint
No additional constraint.
@ Perpendicular
Perpendicular.
WkbType
The WKB type describes the number of dimensions a geometry has.
Draws the graphical elements of the CAD tools (.
void updatePosition() override
called on changed extent or resize event to update position of the item
A widget that floats next to the mouse pointer, and allows interaction with the AdvancedDigitizing fe...
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.
Structure with details of one constraint.
bool locked
Whether the constraint is active, i.e. should be considered.
double value
Numeric value of the constraint (coordinate/distance in map units or angle in degrees)
bool relative
Whether the value is relative to previous value.
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.
Qgis::LineExtensionSide softLockLineExtension
QgsPointXY finalMapPoint
map point aligned according to the constraints
bool valid
Whether the combination of constraints is actually valid.
QgsPointLocator::Match snapMatch
Snapped point - only valid if actually used for something.
double softLockCommonAngle
Angle (in degrees) to which we have soft-locked ourselves (if not set it is -1)
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.
Handles parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Wraps a request for features to a vector layer (or directly its vector data provider).
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
An 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.
A geometry is the spatial representation of a feature.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
static QgsGui * instance()
Returns a pointer to the singleton instance.
static QgsAdvancedDigitizingToolsRegistry * advancedDigitizingToolsRegistry()
Returns the global advanced digitizing tools registry, used for registering advanced digitizing tools...
void clear() override
Clears the geometry, ie reset it to a null geometry.
bool isEmpty() const override
Returns true if the geometry is empty.
int numPoints() const override
Returns the number of points in the curve.
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
Map canvas is a class for displaying all GIS data types on a canvas.
QgsMapTool * mapTool() const
Returns the currently active tool.
QgsSnappingUtils * snappingUtils() const
Returns snapping utility class that is associated with map canvas.
void destinationCrsChanged()
Emitted when map CRS has changed.
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.
A mouse event which is the result of a user interaction with a QgsMapCanvas.
QgsPointXY originalMapPoint() const
Returns the original, unmodified map point of the mouse cursor.
QgsPointXY mapPoint() const
mapPoint returns the point in coordinates
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
Qgis::DistanceUnit mapUnits() const
Returns the units of the map's geographical coordinates - used for scale calculation.
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.
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.
Point geometry type, with support for z-dimension and m-values.
void setY(double y)
Sets the point's y-coordinate.
void setX(double x)
Sets the point's x-coordinate.
void setM(double m)
Sets the point's m-value.
void setZ(double z)
Sets the point's z-coordinate.
double distanceSquared(double x, double y) const
Returns the Cartesian 2D squared distance between this point a specified x, y coordinate.
const QgsBearingNumericFormat * bearingFormat() const
Returns the project bearing's format, which controls how bearings associated with the project are dis...
Qgis::DistanceUnit distanceUnits
static QgsProject * instance()
Returns the QgsProject singleton instance.
void snappingConfigChanged(const QgsSnappingConfig &config)
Emitted whenever the configuration for snapping has changed.
QgsSnappingConfig snappingConfig
void cleared()
Emitted when the project is cleared (and additionally when an open project is cleared just before a n...
QgsProjectDisplaySettings * displaySettings
QgsCoordinateTransformContext transformContext
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
Stores settings for use within QGIS.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Shows a snapping marker on map canvas for the current snapping match.
Stores configuration of snapping settings for the project.
void setTypeFlag(Qgis::SnappingTypes type)
define the type of snapping
void setMode(Qgis::SnappingMode mode)
define the mode of snapping
Contains configuration of snapping and can return answers to snapping queries.
void addExtraSnapLayer(QgsVectorLayer *vl)
Supply an extra snapping layer (typically a memory layer).
void removeExtraSnapLayer(QgsVectorLayer *vl)
Removes an extra snapping layer.
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 double fromUnitToUnitFactor(Qgis::DistanceUnit fromUnit, Qgis::DistanceUnit toUnit)
Returns the conversion factor between the specified distance units.
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 dataset.
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...
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
QSet< QgsFeatureId > QgsFeatureIds
QLineF segment(int index, QRectF rect, double radius)
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.
Setting options for loading vector layers.