43#include <QActionGroup>
44#include <QCoreApplication>
48#include "moc_qgsadvanceddigitizingdockwidget.cpp"
58 , mMapCanvas( canvas )
59 , mUserInputWidget( userInputWidget )
61 , mCommonAngleConstraint(
QgsSettings().value( QStringLiteral(
"/Cad/CommonAngle" ), 0.0 ).toDouble() )
67 mAngleConstraint = std::make_unique<CadConstraint>( mAngleLineEdit, mLockAngleButton, mRelativeAngleButton, mRepeatingLockAngleButton );
69 mAngleConstraint->setMapCanvas( mMapCanvas );
70 mDistanceConstraint = std::make_unique<CadConstraint>( mDistanceLineEdit, mLockDistanceButton,
nullptr, mRepeatingLockDistanceButton );
72 mDistanceConstraint->setMapCanvas( mMapCanvas );
73 mXConstraint = std::make_unique<CadConstraint>( mXLineEdit, mLockXButton, mRelativeXButton, mRepeatingLockXButton );
75 mXConstraint->setMapCanvas( mMapCanvas );
76 mYConstraint = std::make_unique<CadConstraint>( mYLineEdit, mLockYButton, mRelativeYButton, mRepeatingLockYButton );
78 mYConstraint->setMapCanvas( mMapCanvas );
79 mZConstraint = std::make_unique<CadConstraint>( mZLineEdit, mLockZButton, mRelativeZButton, mRepeatingLockZButton );
81 mZConstraint->setMapCanvas( mMapCanvas );
82 mMConstraint = std::make_unique<CadConstraint>( mMLineEdit, mLockMButton, mRelativeMButton, mRepeatingLockMButton );
84 mMConstraint->setMapCanvas( mMapCanvas );
86 mLineExtensionConstraint = std::make_unique<CadConstraint>(
new QLineEdit(),
new QToolButton() );
87 mXyVertexConstraint = std::make_unique<CadConstraint>(
new QLineEdit(),
new QToolButton() );
88 mXyVertexConstraint->setMapCanvas( mMapCanvas );
92 mMapCanvas->installEventFilter(
this );
93 mAngleLineEdit->installEventFilter(
this );
94 mDistanceLineEdit->installEventFilter(
this );
95 mXLineEdit->installEventFilter(
this );
96 mYLineEdit->installEventFilter(
this );
97 mZLineEdit->installEventFilter(
this );
98 mMLineEdit->installEventFilter(
this );
101 connect( mEnableAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::activateCad );
102 connect( mConstructionModeAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::setConstructionMode );
103 connect( mParallelAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
104 connect( mPerpendicularAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
105 connect( mLockAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
106 connect( mLockDistanceButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
107 connect( mLockXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
108 connect( mLockYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
109 connect( mLockZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
110 connect( mLockMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
111 connect( mRelativeAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
112 connect( mRelativeXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
113 connect( mRelativeYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
114 connect( mRelativeZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
115 connect( mRelativeMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
116 connect( mRepeatingLockDistanceButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
117 connect( mRepeatingLockAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
118 connect( mRepeatingLockXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
119 connect( mRepeatingLockYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
120 connect( mRepeatingLockZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
121 connect( mRepeatingLockMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
122 connect( mAngleLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
123 connect( mDistanceLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
124 connect( mXLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
125 connect( mYLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
126 connect( mZLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
127 connect( mMLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
128 connect( mAngleLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
129 connect( mDistanceLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
130 connect( mXLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
131 connect( mYLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
132 connect( mZLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
133 connect( mMLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
139 whileBlocking( mAngleLineEdit )->setText( cleanedInputValue );
145 whileBlocking( mDistanceLineEdit )->setText( cleanedInputValue );
157 mCommonAngleActionsMenu =
new QMenu(
this );
159#ifndef __clang_analyzer__
160 QActionGroup *angleButtonGroup =
new QActionGroup( mCommonAngleActionsMenu );
162 QList<QPair<double, QString>> commonAngles;
163 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 } );
164 for ( QList<double>::const_iterator it = anglesDouble.constBegin(); it != anglesDouble.constEnd(); ++it )
170 QMenu *snappingPriorityMenu =
new QMenu( tr(
"Snapping Priority" ), mCommonAngleActionsMenu );
171 QActionGroup *snappingPriorityActionGroup =
new QActionGroup( snappingPriorityMenu );
172 QAction *featuresAction =
new QAction( tr(
"Prioritize Snapping to Features" ), snappingPriorityActionGroup );
173 featuresAction->setCheckable(
true );
174 QAction *anglesAction =
new QAction( tr(
"Prioritize Snapping to Common Angles" ), snappingPriorityActionGroup );
175 anglesAction->setCheckable(
true );
176 snappingPriorityActionGroup->addAction( featuresAction );
177 snappingPriorityActionGroup->addAction( anglesAction );
178 snappingPriorityMenu->addAction( anglesAction );
179 snappingPriorityMenu->addAction( featuresAction );
180 connect( anglesAction, &QAction::changed,
this, [
this, featuresAction] {
181 mSnappingPrioritizeFeatures = featuresAction->isChecked();
182 settingsCadSnappingPriorityPrioritizeFeature->setValue( featuresAction->isChecked() );
184 featuresAction->setChecked( settingsCadSnappingPriorityPrioritizeFeature->value() );
185 anglesAction->setChecked( !featuresAction->isChecked() );
186 mCommonAngleActionsMenu->addMenu( snappingPriorityMenu );
189 for ( QList<QPair<double, QString>>::const_iterator it = commonAngles.constBegin(); it != commonAngles.constEnd(); ++it )
191 QAction *action =
new QAction( it->second, mCommonAngleActionsMenu );
192 action->setCheckable(
true );
193 action->setChecked( it->first == mCommonAngleConstraint );
194 mCommonAngleActionsMenu->addAction( action );
196#ifndef __clang_analyzer__
197 angleButtonGroup->addAction( action );
199 mCommonAngleActions.insert( it->first, action );
203 QMenu *constructionSettingsMenu =
new QMenu(
this );
205 mRecordConstructionGuides =
new QAction( tr(
"Record Construction Guides" ), constructionSettingsMenu );
206 mRecordConstructionGuides->setCheckable(
true );
207 mRecordConstructionGuides->setChecked( settingsCadRecordConstructionGuides->value() );
208 constructionSettingsMenu->addAction( mRecordConstructionGuides );
209 connect( mRecordConstructionGuides, &QAction::triggered,
this, [
this]() { settingsCadRecordConstructionGuides->setValue( mRecordConstructionGuides->isChecked() ); } );
211 mShowConstructionGuides =
new QAction( tr(
"Show Construction Guides" ), constructionSettingsMenu );
212 mShowConstructionGuides->setCheckable(
true );
213 mShowConstructionGuides->setChecked( settingsCadShowConstructionGuides->value() );
214 constructionSettingsMenu->addAction( mShowConstructionGuides );
215 connect( mShowConstructionGuides, &QAction::triggered,
this, [
this]() {
216 settingsCadShowConstructionGuides->setValue( mShowConstructionGuides->isChecked() );
220 mSnapToConstructionGuides =
new QAction( tr(
"Snap to Visible Construction Guides" ), constructionSettingsMenu );
221 mSnapToConstructionGuides->setCheckable(
true );
222 mSnapToConstructionGuides->setChecked( settingsCadSnapToConstructionGuides->value() );
223 constructionSettingsMenu->addAction( mSnapToConstructionGuides );
224 connect( mSnapToConstructionGuides, &QAction::triggered,
this, [
this]() { settingsCadSnapToConstructionGuides->setValue( mSnapToConstructionGuides->isChecked() ); } );
226 constructionSettingsMenu->addSeparator();
228 mClearConstructionGuides =
new QAction( tr(
"Clear Construction Guides" ), constructionSettingsMenu );
229 constructionSettingsMenu->addAction( mClearConstructionGuides );
230 connect( mClearConstructionGuides, &QAction::triggered,
this, [
this]() {
231 resetConstructionGuides();
235 QToolButton *constructionModeToolButton = qobject_cast<QToolButton *>( mToolbar->widgetForAction( mConstructionModeAction ) );
236 constructionModeToolButton->setPopupMode( QToolButton::MenuButtonPopup );
237 constructionModeToolButton->setMenu( constructionSettingsMenu );
238 constructionModeToolButton->setObjectName( QStringLiteral(
"ConstructionModeButton" ) );
241 QMenu *toolsMenu =
new QMenu(
this );
242 connect( toolsMenu, &QMenu::aboutToShow,
this, [
this, toolsMenu]() {
245 for (
const QString &name : toolMetadataNames )
248 QAction *toolAction =
new QAction( toolMetadata->
icon(), toolMetadata->
visibleName(), toolsMenu );
249 connect( toolAction, &QAction::triggered,
this, [
this, toolMetadata]() {
252 toolsMenu->addAction( toolAction );
255 qobject_cast<QToolButton *>( mToolbar->widgetForAction( mToolsAction ) )->setPopupMode( QToolButton::InstantPopup );
256 mToolsAction->setMenu( toolsMenu );
258 qobject_cast<QToolButton *>( mToolbar->widgetForAction( mSettingsAction ) )->setPopupMode( QToolButton::InstantPopup );
259 mSettingsAction->setMenu( mCommonAngleActionsMenu );
260 mSettingsAction->setCheckable(
true );
261 mSettingsAction->setToolTip(
"<b>" + tr(
"Snap to common angles" ) +
"</b><br>(" + tr(
"press n to cycle through the options" ) +
")" );
262 mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
263 connect( mCommonAngleActionsMenu, &QMenu::triggered,
this, &QgsAdvancedDigitizingDockWidget::settingsButtonTriggered );
266 QMenu *constructionMenu =
new QMenu(
this );
268 mLineExtensionAction =
new QAction( tr(
"Line Extension" ), constructionMenu );
269 mLineExtensionAction->setCheckable(
true );
270 constructionMenu->addAction( mLineExtensionAction );
271 connect( mLineExtensionAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
273 mXyVertexAction =
new QAction( tr(
"X/Y Point" ), constructionMenu );
274 mXyVertexAction->setCheckable(
true );
275 constructionMenu->addAction( mXyVertexAction );
276 connect( mXyVertexAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
278 auto constructionToolBar = qobject_cast<QToolButton *>( mToolbar->widgetForAction( mConstructionAction ) );
279 constructionToolBar->setPopupMode( QToolButton::InstantPopup );
280 mConstructionAction->setMenu( constructionMenu );
281 constructionToolBar->setObjectName( QStringLiteral(
"ConstructionButton" ) );
282 mConstructionAction->setCheckable(
true );
283 mConstructionAction->setToolTip( tr(
"Construction Tools" ) );
286 mConstructionModeAction->setToolTip(
"<b>" + tr(
"Construction mode" ) +
"</b><br>(" + tr(
"press c to toggle on/off" ) +
")" );
287 mDistanceLineEdit->setToolTip(
"<b>" + tr(
"Distance" ) +
"</b><br>(" + tr(
"press d for quick access" ) +
")" );
288 mLockDistanceButton->setToolTip(
"<b>" + tr(
"Lock distance" ) +
"</b><br>(" + tr(
"press Ctrl + d for quick access" ) +
")" );
289 mRepeatingLockDistanceButton->setToolTip(
"<b>" + tr(
"Continuously lock distance" ) +
"</b>" );
291 mRelativeAngleButton->setToolTip(
"<b>" + tr(
"Toggles relative angle to previous segment" ) +
"</b><br>(" + tr(
"press Shift + a for quick access" ) +
")" );
292 mAngleLineEdit->setToolTip(
"<b>" + tr(
"Angle" ) +
"</b><br>(" + tr(
"press a for quick access" ) +
")" );
293 mLockAngleButton->setToolTip(
"<b>" + tr(
"Lock angle" ) +
"</b><br>(" + tr(
"press Ctrl + a for quick access" ) +
")" );
294 mRepeatingLockAngleButton->setToolTip(
"<b>" + tr(
"Continuously lock angle" ) +
"</b>" );
296 mRelativeXButton->setToolTip(
"<b>" + tr(
"Toggles relative x to previous node" ) +
"</b><br>(" + tr(
"press Shift + x for quick access" ) +
")" );
297 mXLineEdit->setToolTip(
"<b>" + tr(
"X coordinate" ) +
"</b><br>(" + tr(
"press x for quick access" ) +
")" );
298 mLockXButton->setToolTip(
"<b>" + tr(
"Lock x coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + x for quick access" ) +
")" );
299 mRepeatingLockXButton->setToolTip(
"<b>" + tr(
"Continuously lock x coordinate" ) +
"</b>" );
301 mRelativeYButton->setToolTip(
"<b>" + tr(
"Toggles relative y to previous node" ) +
"</b><br>(" + tr(
"press Shift + y for quick access" ) +
")" );
302 mYLineEdit->setToolTip(
"<b>" + tr(
"Y coordinate" ) +
"</b><br>(" + tr(
"press y for quick access" ) +
")" );
303 mLockYButton->setToolTip(
"<b>" + tr(
"Lock y coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + y for quick access" ) +
")" );
304 mRepeatingLockYButton->setToolTip(
"<b>" + tr(
"Continuously lock y coordinate" ) +
"</b>" );
306 mRelativeZButton->setToolTip(
"<b>" + tr(
"Toggles relative z to previous node" ) +
"</b><br>(" + tr(
"press Shift + z for quick access" ) +
")" );
307 mZLineEdit->setToolTip(
"<b>" + tr(
"Z coordinate" ) +
"</b><br>(" + tr(
"press z for quick access" ) +
")" );
308 mLockZButton->setToolTip(
"<b>" + tr(
"Lock z coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + z for quick access" ) +
")" );
309 mRepeatingLockZButton->setToolTip(
"<b>" + tr(
"Continuously lock z coordinate" ) +
"</b>" );
311 mRelativeMButton->setToolTip(
"<b>" + tr(
"Toggles relative m to previous node" ) +
"</b><br>(" + tr(
"press Shift + m for quick access" ) +
")" );
312 mMLineEdit->setToolTip(
"<b>" + tr(
"M coordinate" ) +
"</b><br>(" + tr(
"press m for quick access" ) +
")" );
313 mLockMButton->setToolTip(
"<b>" + tr(
"Lock m coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + m for quick access" ) +
")" );
314 mRepeatingLockMButton->setToolTip(
"<b>" + tr(
"Continuously lock m coordinate" ) +
"</b>" );
325 mFloaterActionsMenu =
new QMenu(
this );
326 qobject_cast<QToolButton *>( mToolbar->widgetForAction( mFloaterAction ) )->setPopupMode( QToolButton::InstantPopup );
327 mFloaterAction->setMenu( mFloaterActionsMenu );
328 mFloaterAction->setCheckable(
true );
330 mFloaterAction->setChecked( mFloater->active() );
334 QAction *action =
new QAction( tr(
"Show floater" ), mFloaterActionsMenu );
335 action->setCheckable(
true );
336 action->setChecked( mFloater->active() );
337 mFloaterActionsMenu->addAction( action );
338 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
339 mFloater->setActive( checked );
340 mFloaterAction->setChecked( checked );
344 mFloaterActionsMenu->addSeparator();
347 QAction *action =
new QAction( tr(
"Show distance" ), mFloaterActionsMenu );
348 action->setCheckable(
true );
349 mFloaterActionsMenu->addAction( action );
350 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
353 const bool isDistanceChecked =
QgsSettings().
value( QStringLiteral(
"/Cad/DistanceShowInFloater" ),
true ).toBool();
354 action->setChecked( isDistanceChecked );
360 QAction *action =
new QAction( tr(
"Show angle" ), mFloaterActionsMenu );
361 action->setCheckable(
true );
362 mFloaterActionsMenu->addAction( action );
363 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
366 const bool isAngleChecked =
QgsSettings().
value( QStringLiteral(
"/Cad/AngleShowInFloater" ),
true ).toBool();
367 action->setChecked( isAngleChecked );
373 QAction *action =
new QAction( tr(
"Show XY coordinates" ), mFloaterActionsMenu );
374 action->setCheckable(
true );
375 mFloaterActionsMenu->addAction( action );
376 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
381 const bool isXCoordinateChecked =
QgsSettings().
value( QStringLiteral(
"/Cad/XCoordinateShowInFloater" ),
true ).toBool();
382 action->setChecked( isXCoordinateChecked );
390 QAction *action =
new QAction( tr(
"Show Z value" ), mFloaterActionsMenu );
391 action->setCheckable(
true );
392 mFloaterActionsMenu->addAction( action );
393 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
396 const bool isZCoordinateChecked =
QgsSettings().
value( QStringLiteral(
"/Cad/ZCoordinateShowInFloater" ),
true ).toBool();
397 action->setChecked( isZCoordinateChecked );
404 QAction *action =
new QAction( tr(
"Show M value" ), mFloaterActionsMenu );
405 action->setCheckable(
true );
406 mFloaterActionsMenu->addAction( action );
407 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
410 const bool isMCoordinateChecked =
QgsSettings().
value( QStringLiteral(
"/Cad/MCoordinateShowInFloater" ),
true ).toBool();
411 action->setChecked( isMCoordinateChecked );
418 QAction *action =
new QAction( tr(
"Show bearing/azimuth" ), mFloaterActionsMenu );
419 action->setCheckable(
true );
420 mFloaterActionsMenu->addAction( action );
421 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
424 const bool isBearingChecked =
QgsSettings().
value( QStringLiteral(
"/Cad/BearingShowInFloater" ),
false ).toBool();
425 action->setChecked( isBearingChecked );
432 QAction *action =
new QAction( tr(
"Show common snapping angle" ), mFloaterActionsMenu );
433 action->setCheckable(
true );
434 mFloaterActionsMenu->addAction( action );
435 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
438 const bool isCommonAngleSnappingChecked =
QgsSettings().
value( QStringLiteral(
"/Cad/CommonAngleSnappingShowInFloater" ),
false ).toBool();
439 action->setChecked( isCommonAngleSnappingChecked );
445 updateCapacity(
true );
449 mConstructionGuidesLayer.reset();
460 mCurrentTool->deleteLater();
467 return tr(
"Do Not Snap to Common Angles" );
469 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 );
474 mXLineEdit->setText( value );
477 emit mXLineEdit->returnPressed();
481 QEvent *e =
new QEvent( QEvent::FocusOut );
482 QCoreApplication::postEvent( mXLineEdit, e );
486 emit mXLineEdit->textEdited( value );
491 mYLineEdit->setText( value );
494 emit mYLineEdit->returnPressed();
498 QEvent *e =
new QEvent( QEvent::FocusOut );
499 QCoreApplication::postEvent( mYLineEdit, e );
503 emit mYLineEdit->textEdited( value );
508 mZLineEdit->setText( value );
511 emit mZLineEdit->returnPressed();
515 QEvent *e =
new QEvent( QEvent::FocusOut );
516 QCoreApplication::postEvent( mZLineEdit, e );
520 emit mZLineEdit->textEdited( value );
525 mMLineEdit->setText( value );
528 emit mMLineEdit->returnPressed();
532 QEvent *e =
new QEvent( QEvent::FocusOut );
533 QCoreApplication::postEvent( mMLineEdit, e );
537 emit mMLineEdit->textEdited( value );
542 mAngleLineEdit->setText( value );
545 emit mAngleLineEdit->returnPressed();
549 emit mAngleLineEdit->textEdited( value );
554 mDistanceLineEdit->setText( value );
557 emit mDistanceLineEdit->returnPressed();
561 QEvent *e =
new QEvent( QEvent::FocusOut );
562 QCoreApplication::postEvent( mDistanceLineEdit, e );
566 emit mDistanceLineEdit->textEdited( value );
571void QgsAdvancedDigitizingDockWidget::setCadEnabled(
bool enabled )
573 mCadEnabled = enabled;
574 mEnableAction->setChecked( enabled );
575 mConstructionModeAction->setEnabled( enabled );
576 mSettingsAction->setEnabled( enabled );
577 mInputWidgets->setEnabled( enabled );
578 mFloaterAction->setEnabled( enabled );
579 mConstructionAction->setEnabled( enabled );
580 mToolsAction->setEnabled( enabled );
585 mLineExtensionAction->setChecked(
false );
586 mXyVertexAction->setChecked(
false );
588 mParallelAction->setEnabled(
false );
589 mPerpendicularAction->setEnabled(
false );
592 mCurrentTool->deleteLater();
599 setConstructionMode(
false );
609 mLastSnapMatch = QgsPointLocator::Match();
615 bool enableZ =
false;
616 bool enableM =
false;
620 switch ( layer->type() )
633 QgsMeshLayer *mlayer = qobject_cast<QgsMeshLayer *>( layer );
649 mTargetLayerSupportsM = enableM;
650 mTargetLayerSupportsZ = enableZ;
658 if (
enable && !mTargetLayerSupportsZ )
662 mRelativeZButton->setEnabled(
enable );
663 mZLabel->setEnabled(
enable );
664 mZLineEdit->setEnabled(
enable );
665 if ( mZLineEdit->isEnabled() )
669 mLockZButton->setEnabled(
enable );
675 if (
enable && !mTargetLayerSupportsM )
679 mRelativeMButton->setEnabled(
enable );
680 mMLabel->setEnabled(
enable );
681 mMLineEdit->setEnabled(
enable );
682 if ( mMLineEdit->isEnabled() )
686 mLockMButton->setEnabled(
enable );
690void QgsAdvancedDigitizingDockWidget::activateCad(
bool enabled )
692 enabled &= mCurrentMapToolSupportsCad;
694 mSessionActive = enabled;
696 if ( enabled && !isVisible() )
701 setCadEnabled( enabled );
708 mCurrentTool->deleteLater();
709 mCurrentTool =
nullptr;
716 if ( QWidget *toolWidget = mCurrentTool->createWidget() )
718 toolWidget->setParent( mUserInputWidget );
719 mUserInputWidget->addUserInputWidget( toolWidget );
727 return mCurrentTool.data();
730void QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked(
bool activated )
736 else if ( sender() == mParallelAction )
740 else if ( sender() == mPerpendicularAction )
746void QgsAdvancedDigitizingDockWidget::setConstraintRelative(
bool activate )
748 if ( sender() == mRelativeAngleButton )
750 mAngleConstraint->setRelative( activate );
753 else if ( sender() == mRelativeXButton )
755 mXConstraint->setRelative( activate );
758 else if ( sender() == mRelativeYButton )
760 mYConstraint->setRelative( activate );
763 else if ( sender() == mRelativeZButton )
765 mZConstraint->setRelative( activate );
768 else if ( sender() == mRelativeMButton )
770 mMConstraint->setRelative( activate );
775void QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock(
bool activate )
777 if ( sender() == mRepeatingLockDistanceButton )
779 mDistanceConstraint->setRepeatingLock( activate );
781 else if ( sender() == mRepeatingLockAngleButton )
783 mAngleConstraint->setRepeatingLock( activate );
785 else if ( sender() == mRepeatingLockXButton )
787 mXConstraint->setRepeatingLock( activate );
789 else if ( sender() == mRepeatingLockYButton )
791 mYConstraint->setRepeatingLock( activate );
793 else if ( sender() == mRepeatingLockZButton )
795 mZConstraint->setRepeatingLock( activate );
797 else if ( sender() == mRepeatingLockMButton )
799 mMConstraint->setRepeatingLock( activate );
803void QgsAdvancedDigitizingDockWidget::setConstructionMode(
bool enabled )
805 mConstructionMode = enabled;
806 mConstructionModeAction->setChecked( enabled );
810 if ( enabled && mCadPointList.size() > 1 )
812 mConstructionGuideLine.addVertex( mCadPointList.at( 1 ) );
817void QgsAdvancedDigitizingDockWidget::settingsButtonTriggered( QAction *action )
820 for (
auto it = mCommonAngleActions.cbegin(); it != mCommonAngleActions.cend(); ++it )
822 if ( it.value() == action )
824 it.value()->setChecked(
true );
825 mCommonAngleConstraint = it.key();
826 QgsSettings().setValue( QStringLiteral(
"/Cad/CommonAngle" ), it.key() );
827 mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
834QgsMapLayer *QgsAdvancedDigitizingDockWidget::targetLayer()
const
836 if ( QgsMapToolAdvancedDigitizing *advancedTool = qobject_cast<QgsMapToolAdvancedDigitizing *>( mMapCanvas->mapTool() ) )
838 return advancedTool->layer();
842 return mMapCanvas->currentLayer();
852 if ( releaseRepeatingLocks )
854 mXyVertexAction->setChecked(
false );
858 mLineExtensionAction->setChecked(
false );
865 if ( releaseRepeatingLocks || !mAngleConstraint->isRepeatingLock() )
870 if ( releaseRepeatingLocks || !mDistanceConstraint->isRepeatingLock() )
875 if ( releaseRepeatingLocks || !mXConstraint->isRepeatingLock() )
880 if ( releaseRepeatingLocks || !mYConstraint->isRepeatingLock() )
885 if ( releaseRepeatingLocks || !mZConstraint->isRepeatingLock() )
890 if ( releaseRepeatingLocks || !mMConstraint->isRepeatingLock() )
896 if ( !mCadPointList.empty() )
898 if ( !mXConstraint->isLocked() && !mXConstraint->relative() )
900 mXConstraint->setValue( mCadPointList.constLast().x(),
true );
902 if ( !mYConstraint->isLocked() && !mYConstraint->relative() )
904 mYConstraint->setValue( mCadPointList.constLast().y(),
true );
906 if ( !mZConstraint->isLocked() && !mZConstraint->relative() )
908 mZConstraint->setValue( mCadPointList.constLast().z(),
true );
910 if ( !mMConstraint->isLocked() && !mMConstraint->relative() )
912 mMConstraint->setValue( mCadPointList.constLast().m(),
true );
918void QgsAdvancedDigitizingDockWidget::emit pointChanged()
921 QPoint globalPos = mMapCanvas->cursor().pos();
922 QPoint pos = mMapCanvas->mapFromGlobal( globalPos );
923 QMouseEvent *e =
new QMouseEvent( QEvent::MouseMove, pos, globalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier );
924 mCurrentMapTool->canvasMoveEvent( e );
931 if ( obj == mAngleLineEdit || obj == mLockAngleButton )
933 constraint = mAngleConstraint.get();
935 else if ( obj == mDistanceLineEdit || obj == mLockDistanceButton )
937 constraint = mDistanceConstraint.get();
939 else if ( obj == mXLineEdit || obj == mLockXButton )
941 constraint = mXConstraint.get();
943 else if ( obj == mYLineEdit || obj == mLockYButton )
945 constraint = mYConstraint.get();
947 else if ( obj == mZLineEdit || obj == mLockZButton )
949 constraint = mZConstraint.get();
951 else if ( obj == mMLineEdit || obj == mLockMButton )
953 constraint = mMConstraint.get();
955 else if ( obj == mLineExtensionAction )
957 constraint = mLineExtensionConstraint.get();
959 else if ( obj == mXyVertexAction )
961 constraint = mXyVertexConstraint.get();
966double QgsAdvancedDigitizingDockWidget::parseUserInput(
const QString &inputValue,
const Qgis::CadConstraintType type,
bool &ok )
const
976 QgsExpression expr( inputValue );
977 const QVariant result = expr.evaluate();
978 if ( expr.hasEvalError() )
981 QString inputValueC { inputValue };
984 if ( inputValue.contains( QLocale().groupSeparator() ) )
986 inputValueC.remove( QLocale().groupSeparator() );
987 QgsExpression exprC( inputValueC );
988 const QVariant resultC = exprC.evaluate();
989 if ( !exprC.hasEvalError() )
991 value = resultC.toDouble( &ok );
996 if ( !ok && QLocale().decimalPoint() != QChar(
'.' ) && inputValueC.contains( QLocale().decimalPoint() ) )
998 QgsExpression exprC( inputValueC.replace( QLocale().decimalPoint(), QChar(
'.' ) ) );
999 const QVariant resultC = exprC.evaluate();
1000 if ( !exprC.hasEvalError() )
1002 value = resultC.toDouble( &ok );
1008 value = result.toDouble( &ok );
1023void QgsAdvancedDigitizingDockWidget::updateConstraintValue( CadConstraint *constraint,
const QString &textValue,
bool convertExpression )
1025 if ( !constraint || textValue.isEmpty() )
1034 const double value = parseUserInput( textValue, constraint->cadConstraintType(), ok );
1038 constraint->setValue( value, convertExpression );
1043void QgsAdvancedDigitizingDockWidget::lockConstraint(
bool activate )
1053 const QString textValue = constraint->lineEdit()->text();
1054 if ( !textValue.isEmpty() )
1057 const double value = parseUserInput( textValue, constraint->cadConstraintType(), ok );
1060 constraint->setValue( value );
1074 if ( constraint == mXConstraint.get() )
1078 else if ( constraint == mYConstraint.get() )
1082 else if ( constraint == mZConstraint.get() )
1086 else if ( constraint == mMConstraint.get() )
1090 else if ( constraint == mDistanceConstraint.get() )
1094 else if ( constraint == mAngleConstraint.get() )
1102 if ( constraint == mAngleConstraint.get() )
1112void QgsAdvancedDigitizingDockWidget::constraintTextEdited(
const QString &textValue )
1120 updateConstraintValue( constraint, textValue,
false );
1123void QgsAdvancedDigitizingDockWidget::constraintFocusOut()
1125 QLineEdit *lineEdit = qobject_cast<QLineEdit *>( sender()->parent() );
1135 updateConstraintValue( constraint, lineEdit->text(),
true );
1140 mBetweenLineConstraint = constraint;
1145void QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint(
bool activate )
1155 if ( constraint == mXyVertexConstraint.get() )
1159 else if ( constraint == mLineExtensionConstraint.get() )
1173void QgsAdvancedDigitizingDockWidget::updateCapacity(
bool updateUIwithoutChange )
1176 const bool isGeographic = mMapCanvas->mapSettings().destinationCrs().isGeographic();
1179 if ( mCadPointList.count() > 1 )
1182 if ( !isGeographic )
1188 if ( mCadPointList.count() > 2 )
1190 if ( !isGeographic )
1193 if ( !updateUIwithoutChange && newCapacities == mCapacities )
1203 const bool distance = mCadEnabled && newCapacities.testFlag(
Distance );
1204 const bool relativeAngle = mCadEnabled && newCapacities.testFlag(
RelativeAngle );
1205 const bool absoluteAngle = mCadEnabled && newCapacities.testFlag(
AbsoluteAngle );
1206 const bool relativeCoordinates = mCadEnabled && newCapacities.testFlag(
RelativeCoordinates );
1208 mPerpendicularAction->setEnabled( distance && snappingEnabled );
1209 mParallelAction->setEnabled( distance && snappingEnabled );
1211 mLineExtensionAction->setEnabled( snappingEnabled );
1212 mXyVertexAction->setEnabled( snappingEnabled );
1216 if ( !snappingEnabled )
1218 mPerpendicularAction->setToolTip( tr(
"Snapping must be enabled to utilize perpendicular mode." ) );
1219 mParallelAction->setToolTip( tr(
"Snapping must be enabled to utilize parallel mode." ) );
1220 mLineExtensionAction->setToolTip( tr(
"Snapping must be enabled to utilize line extension mode." ) );
1221 mXyVertexAction->setToolTip( tr(
"Snapping must be enabled to utilize xy point mode." ) );
1223 else if ( mCadPointList.count() <= 1 )
1225 mPerpendicularAction->setToolTip( tr(
"A first vertex should be drawn to utilize perpendicular mode." ) );
1226 mParallelAction->setToolTip( tr(
"A first vertex should be drawn to utilize parallel mode." ) );
1228 else if ( isGeographic )
1230 mPerpendicularAction->setToolTip( tr(
"Perpendicular mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1231 mParallelAction->setToolTip( tr(
"Parallel mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1235 mPerpendicularAction->setToolTip(
"<b>" + tr(
"Perpendicular" ) +
"</b><br>(" + tr(
"press p to switch between perpendicular, parallel and normal mode" ) +
")" );
1236 mParallelAction->setToolTip(
"<b>" + tr(
"Parallel" ) +
"</b><br>(" + tr(
"press p to switch between perpendicular, parallel and normal mode" ) +
")" );
1240 if ( !absoluteAngle )
1246 mLockAngleButton->setEnabled( absoluteAngle );
1247 mRelativeAngleButton->setEnabled( relativeAngle );
1248 mAngleLineEdit->setEnabled( absoluteAngle );
1250 if ( !absoluteAngle )
1254 if ( !relativeAngle )
1256 mAngleConstraint->setRelative(
false );
1259 else if ( relativeAngle && !mCapacities.testFlag(
RelativeAngle ) )
1262 mAngleConstraint->setRelative(
true );
1267 mLockDistanceButton->setEnabled( distance && relativeCoordinates );
1268 mDistanceLineEdit->setEnabled( distance && relativeCoordinates );
1270 if ( !( distance && relativeCoordinates ) )
1275 mRelativeXButton->setEnabled( relativeCoordinates );
1276 mRelativeYButton->setEnabled( relativeCoordinates );
1277 mRelativeZButton->setEnabled( relativeCoordinates );
1278 mRelativeMButton->setEnabled( relativeCoordinates );
1281 mCapacities = newCapacities;
1282 mCadPaintItem->updatePosition();
1289 constr.
locked =
c->isLocked();
1291 constr.
value =
c->value();
1298 if ( !mLineExtensionConstraint->isLocked() && !mXyVertexConstraint->isLocked() )
1304 const int lastIndex = mLockedSnapVertices.length() - 1;
1305 for (
int i = lastIndex; i >= 0; --i )
1307 if ( mLockedSnapVertices[i].point() == snapMatch.
point() )
1309 if ( snapMatch.
point() != previouslySnap.
point() )
1311 mLockedSnapVertices.removeAt( i );
1317 if ( snapMatch.
point() != previouslySnap.
point() )
1319 mLockedSnapVertices.enqueue( snapMatch );
1322 if ( mLockedSnapVertices.count() > 3 )
1324 mLockedSnapVertices.dequeue();
1333 context.
xConstraint = _constraint( mXConstraint.get() );
1334 context.
yConstraint = _constraint( mYConstraint.get() );
1335 context.
zConstraint = _constraint( mZConstraint.get() );
1336 context.
mConstraint = _constraint( mMConstraint.get() );
1363 const bool res = output.
valid;
1365 mSnappedSegment.clear();
1370 mSnappedSegment << edgePt0 << edgePt1;
1391 mSnapIndicator->setMatch( output.
snapMatch );
1392 mSnapIndicator->setVisible(
true );
1396 mSnapIndicator->setVisible(
false );
1418 if ( mSnapMatch.layer() )
1422 if ( ( ( mSnapMatch.hasVertex() || mSnapMatch.hasLineEndpoint() ) && (
QgsPointXY( point ) == mSnapMatch.point() ) )
1426 point = mSnapMatch.interpolatedPoint( mMapCanvas->mapSettings().destinationCrs() );
1432 if ( mSnapMatch.hasVertex() || mSnapMatch.hasLineEndpoint() )
1434 toggleLockedSnapVertex( mSnapMatch, mLastSnapMatch );
1435 mLastSnapMatch = mSnapMatch;
1445 if ( mLockZButton->isChecked() )
1447 point.
setZ( QLocale().toDouble( mZLineEdit->text() ) );
1449 if ( mLockMButton->isChecked() )
1451 point.
setM( QLocale().toDouble( mMLineEdit->text() ) );
1457 updateUnlockedConstraintValues( point );
1465 emit
pushWarning( tr(
"Some constraints are incompatible. Resulting point might be incorrect." ) );
1472void QgsAdvancedDigitizingDockWidget::updateUnlockedConstraintValues(
const QgsPoint &point )
1474 bool previousPointExist, penulPointExist;
1479 if ( previousPointExist )
1481 double prevAngle = 0.0;
1483 if ( penulPointExist && mAngleConstraint->relative() )
1486 prevAngle = std::atan2( previousPt.
y() - penultimatePt.
y(), previousPt.
x() - penultimatePt.
x() ) * 180 / M_PI;
1489 const double xAngle { std::atan2( point.
y() - previousPt.
y(), point.
x() - previousPt.
x() ) * 180 / M_PI };
1492 const double angle = std::fmod( xAngle - prevAngle, 360.0 );
1493 if ( !mAngleConstraint->isLocked() )
1495 mAngleConstraint->setValue( angle );
1499 double bearing { std::fmod( xAngle, 360.0 ) };
1500 bearing = bearing <= 90.0 ? 90.0 - bearing : ( bearing > 90 ? 270.0 + 180.0 - bearing : 270.0 - bearing );
1501 const QgsNumericFormatContext context;
1506 if ( !mDistanceConstraint->isLocked() && previousPointExist )
1508 mDistanceConstraint->setValue( std::sqrt( previousPt.
distanceSquared( point ) ) );
1511 if ( !mXConstraint->isLocked() )
1513 if ( previousPointExist && mXConstraint->relative() )
1515 mXConstraint->setValue( point.
x() - previousPt.
x() );
1519 mXConstraint->setValue( point.
x() );
1523 if ( !mYConstraint->isLocked() )
1525 if ( previousPointExist && mYConstraint->relative() )
1527 mYConstraint->setValue( point.
y() - previousPt.
y() );
1531 mYConstraint->setValue( point.
y() );
1535 if ( !mZConstraint->isLocked() )
1537 if ( previousPointExist && mZConstraint->relative() )
1539 mZConstraint->setValue( point.
z() - previousPt.
z() );
1543 mZConstraint->setValue( point.
z() );
1547 if ( !mMConstraint->isLocked() )
1549 if ( previousPointExist && mMConstraint->relative() )
1551 mMConstraint->setValue( point.
m() - previousPt.
m() );
1555 mMConstraint->setValue( point.
m() );
1561QList<QgsPointXY> QgsAdvancedDigitizingDockWidget::snapSegmentToAllLayers(
const QgsPointXY &originalMapPoint,
bool *snapped )
const
1564 QgsPointXY pt1, pt2;
1565 QgsPointLocator::Match match;
1567 QgsSnappingUtils *snappingUtils = mMapCanvas->snappingUtils();
1569 const QgsSnappingConfig canvasConfig = snappingUtils->
config();
1570 QgsSnappingConfig localConfig = snappingUtils->
config();
1574 snappingUtils->
setConfig( localConfig );
1576 match = snappingUtils->
snapToMap( originalMapPoint,
nullptr,
true );
1578 snappingUtils->
setConfig( canvasConfig );
1588 *snapped =
segment.count() == 2;
1598 mCurrentTool->canvasPressEvent( event );
1603 event->setAccepted(
false );
1615 mCurrentTool->canvasMoveEvent( event );
1623 if ( event->button() == Qt::RightButton )
1627 mCurrentTool->canvasReleaseEvent( event );
1628 if ( !event->isAccepted() )
1640 event->setAccepted(
false );
1646 mCurrentTool->canvasReleaseEvent( event );
1647 if ( !event->isAccepted() )
1658 if ( mLockZButton->isChecked() )
1660 point.
setZ( QLocale().toDouble( mZLineEdit->text() ) );
1662 if ( mLockMButton->isChecked() )
1664 point.
setM( QLocale().toDouble( mMLineEdit->text() ) );
1675 event->setAccepted(
false );
1687 bool previousPointExist, penulPointExist, snappedSegmentExist;
1690 mSnappedSegment = snapSegmentToAllLayers( e->
originalMapPoint(), &snappedSegmentExist );
1692 if ( !previousPointExist || !snappedSegmentExist )
1697 double angle = std::atan2( mSnappedSegment[0].y() - mSnappedSegment[1].y(), mSnappedSegment[0].x() - mSnappedSegment[1].x() );
1699 if ( mAngleConstraint->relative() && penulPointExist )
1701 angle -= std::atan2( previousPt.
y() - penultimatePt.
y(), previousPt.
x() - penultimatePt.
x() );
1709 angle *= 180 / M_PI;
1711 mAngleConstraint->setValue( angle );
1712 mAngleConstraint->setLockMode( lockMode );
1730 case Qt::Key_Backspace:
1731 case Qt::Key_Delete:
1737 case Qt::Key_Escape:
1756 mCurrentTool->deleteLater();
1759 if ( !mConstructionGuideLine.isEmpty() )
1761 mConstructionGuideLine.clear();
1777 case Qt::Key_Backspace:
1778 case Qt::Key_Delete:
1784 case Qt::Key_Escape:
1788 if ( mConstructionGuideLine.numPoints() >= 2 )
1790 mConstructionGuidesLayer->dataProvider()->deleteFeatures(
QgsFeatureIds() << mConstructionGuideId );
1791 mConstructionGuideLine.clear();
1796 mCurrentTool->deleteLater();
1803 filterKeyPress( e );
1812 const auto constPoints = points;
1821 mDistanceConstraint->toggleLocked();
1826bool QgsAdvancedDigitizingDockWidget::eventFilter( QObject *obj, QEvent *event )
1830 return QgsDockWidget::eventFilter( obj, event );
1838 if ( event->type() == QEvent::ShortcutOverride || event->type() == QEvent::KeyPress )
1840 if ( QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( event ) )
1842 return filterKeyPress( keyEvent );
1845 return QgsDockWidget::eventFilter( obj, event );
1848bool QgsAdvancedDigitizingDockWidget::filterKeyPress( QKeyEvent *e )
1854 const QEvent::Type type = e->type();
1857 case Qt::Key_Escape:
1859 if ( type == QEvent::KeyPress && mCurrentTool )
1861 mCurrentTool->deleteLater();
1863 else if ( type == QEvent::KeyPress && mConstructionMode && mConstructionGuideLine.numPoints() >= 2 )
1865 mConstructionGuidesLayer->dataProvider()->deleteFeatures(
QgsFeatureIds() << mConstructionGuideId );
1866 mConstructionGuideLine.clear();
1868 if ( mCadPointList.size() > 1 )
1870 mConstructionGuideLine.addVertex( mCadPointList.at( 1 ) );
1885 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1887 mXConstraint->toggleLocked();
1892 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1896 mXConstraint->toggleRelative();
1903 else if ( type == QEvent::KeyPress )
1905 mXLineEdit->setFocus();
1906 mXLineEdit->selectAll();
1915 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1917 mYConstraint->toggleLocked();
1922 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1926 mYConstraint->toggleRelative();
1933 else if ( type == QEvent::KeyPress )
1935 mYLineEdit->setFocus();
1936 mYLineEdit->selectAll();
1945 if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::AltModifier )
1947 mZConstraint->toggleLocked();
1952 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1956 mZConstraint->toggleRelative();
1963 else if ( type == QEvent::KeyPress )
1965 mZLineEdit->setFocus();
1966 mZLineEdit->selectAll();
1975 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1977 mMConstraint->toggleLocked();
1982 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1986 mMConstraint->toggleRelative();
1993 else if ( type == QEvent::KeyPress )
1995 mMLineEdit->setFocus();
1996 mMLineEdit->selectAll();
2005 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
2009 mAngleConstraint->toggleLocked();
2015 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
2019 mAngleConstraint->toggleRelative();
2026 else if ( type == QEvent::KeyPress )
2028 mAngleLineEdit->setFocus();
2029 mAngleLineEdit->selectAll();
2038 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
2047 else if ( type == QEvent::KeyPress )
2049 mDistanceLineEdit->setFocus();
2050 mDistanceLineEdit->selectAll();
2058 if ( type == QEvent::KeyPress )
2060 setConstructionMode( !mConstructionMode );
2067 if ( type == QEvent::KeyPress )
2069 const bool parallel = mParallelAction->isChecked();
2070 const bool perpendicular = mPerpendicularAction->isChecked();
2072 if ( !parallel && !perpendicular )
2076 else if ( perpendicular )
2093 if ( type == QEvent::ShortcutOverride )
2095 const QList<double> constActionKeys { mCommonAngleActions.keys() };
2096 const int currentAngleActionIndex {
static_cast<int>( constActionKeys.indexOf( mCommonAngleConstraint ) ) };
2097 const QList<QAction *> constActions { mCommonAngleActions.values() };
2098 QAction *nextAngleAction;
2099 if ( e->modifiers() == Qt::ShiftModifier )
2101 nextAngleAction = currentAngleActionIndex == 0 ? constActions.last() : constActions.at( currentAngleActionIndex - 1 );
2105 nextAngleAction = currentAngleActionIndex == constActions.count() - 1 ? constActions.first() : constActions.at( currentAngleActionIndex + 1 );
2107 nextAngleAction->trigger();
2117 return e->isAccepted();
2124 if ( mMapCanvas->mapSettings().destinationCrs().isGeographic() )
2126 mAngleLineEdit->setToolTip( tr(
"Angle constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
2127 mDistanceLineEdit->setToolTip( tr(
"Distance constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
2129 mLabelX->setText( tr(
"Long" ) );
2130 mLabelY->setText( tr(
"Lat" ) );
2132 mXConstraint->setPrecision( 8 );
2133 mYConstraint->setPrecision( 8 );
2137 mAngleLineEdit->setToolTip(
"<b>" + tr(
"Angle" ) +
"</b><br>(" + tr(
"press a for quick access" ) +
")" );
2138 mAngleLineEdit->setToolTip( QString() );
2140 mDistanceLineEdit->setToolTip(
"<b>" + tr(
"Distance" ) +
"</b><br>(" + tr(
"press d for quick access" ) +
")" );
2142 mLabelX->setText( tr(
"x" ) );
2143 mLabelY->setText( tr(
"y" ) );
2145 mXConstraint->setPrecision( 6 );
2146 mYConstraint->setPrecision( 6 );
2151 mEnableAction->setEnabled(
true );
2152 mErrorLabel->hide();
2155 mCurrentMapToolSupportsCad =
true;
2157 if ( mSessionActive && !isVisible() )
2162 setCadEnabled( mSessionActive );
2164 if ( !mConstructionGuidesLayer )
2166 resetConstructionGuides();
2169 if ( mDeferredUpdateConstructionGuidesCrs )
2171 updateConstructionGuidesCrs();
2181 mEnableAction->setEnabled(
false );
2182 mErrorLabel->setText( tr(
"Advanced digitizing tools are not enabled for the current map tool" ) );
2183 mErrorLabel->show();
2186 mCurrentMapToolSupportsCad =
false;
2188 mSnapIndicator->setVisible(
false );
2190 setCadEnabled(
false );
2195 mCadPaintItem->update();
2200 if ( !force && ( mLineExtensionConstraint->isLocked() || mXyVertexConstraint->isLocked() ) )
2205 mLockedSnapVertices.clear();
2210 QgsPoint pt = pointXYToPoint( point );
2213 mCadPointList << pt;
2217 mCadPointList.insert( 0, pt );
2224 mConstructionGuideLine.addVertex( pt );
2226 if ( mConstructionGuideLine.numPoints() == 2 )
2229 QgsGeometry geom( mConstructionGuideLine.clone() );
2231 ( void ) mConstructionGuidesLayer->dataProvider()->addFeature( feature );
2232 mConstructionGuideId = feature.
id();
2234 else if ( mConstructionGuideLine.numPoints() > 2 )
2236 QgsGeometry geom( mConstructionGuideLine.clone() );
2237 ( void ) mConstructionGuidesLayer->dataProvider()->changeGeometryValues( { { mConstructionGuideId, geom } } );
2242 if ( !mConstructionGuideLine.isEmpty() )
2244 mConstructionGuideLine.addVertex( pt );
2246 QgsGeometry geom( mConstructionGuideLine.clone() );
2247 ( void ) mConstructionGuidesLayer->dataProvider()->changeGeometryValues( { { mConstructionGuideId, geom } } );
2248 mConstructionGuideLine.clear();
2263 mCadPointList.removeAt( i );
2270 mCadPointList.clear();
2271 mSnappedSegment.clear();
2281 mCadPointList << point;
2286 mCadPointList[0] = point;
2293 if ( mode == mLockMode )
2298 mLockerButton->setChecked( mode ==
HardLock );
2299 if ( mRepeatingLockButton )
2303 mRepeatingLockButton->setEnabled(
true );
2307 mRepeatingLockButton->setChecked(
false );
2308 mRepeatingLockButton->setEnabled(
false );
2320 mRepeatingLock = repeating;
2321 if ( mRepeatingLockButton )
2322 mRepeatingLockButton->setChecked( repeating );
2328 if ( mRelativeButton )
2330 mRelativeButton->setChecked(
relative );
2337 if ( updateWidget && mLineEdit->isEnabled() )
2343 switch ( mCadConstraintType )
2347 return QLocale().toString( mValue,
'f', mPrecision ).append( tr(
" °" ) );
2352 if ( mMapCanvas->mapSettings().destinationCrs().isGeographic() )
2354 return QLocale().toString( mValue,
'f', mPrecision ).append( tr(
" °" ) );
2358 return QLocale().toString( mValue,
'f', mPrecision );
2375 return QLocale().toString( mValue,
'f', mPrecision );
2391 if ( mLineEdit->isEnabled() )
2397 return mCadConstraintType;
2402 mCadConstraintType = constraintType;
2407 mMapCanvas = mapCanvas;
2412 QString
value { text.trimmed() };
2413 switch ( constraintType )
2419 if (
value.endsWith( distanceUnit ) )
2421 value.chop( distanceUnit.length() );
2428 const QString angleUnit { tr(
"°" ) };
2429 if (
value.endsWith( angleUnit ) )
2431 value.chop( angleUnit.length() );
2438 return value.trimmed();
2446 return mCadPointList.value( 0 );
2455 QgsPoint res = mCadPointList.value( 0 );
2456 const QgsPointXY layerCoordinates = mMapCanvas->mapSettings().mapToLayerCoordinates( layer, res );
2457 res.
setX( layerCoordinates.
x() );
2458 res.
setY( layerCoordinates.
y() );
2469 return mCadPointList.value( 1 );
2479 return mCadPointList.value( 2 );
2484QgsPoint QgsAdvancedDigitizingDockWidget::pointXYToPoint(
const QgsPointXY &point )
const
2491 return mZLineEdit->isEnabled() ? QLocale().toDouble( mZLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
2496 return mMLineEdit->isEnabled() ? QLocale().toDouble( mMLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
2501 return mShowConstructionGuides ? mShowConstructionGuides->isChecked() :
false;
2506 return mSnapToConstructionGuides ? mShowConstructionGuides->isChecked() && mSnapToConstructionGuides->isChecked() :
false;
2511 return mRecordConstructionGuides ? mRecordConstructionGuides->isChecked() :
false;
2514void QgsAdvancedDigitizingDockWidget::updateConstructionGuidesCrs()
2516 if ( !mConstructionGuidesLayer )
2523 mDeferredUpdateConstructionGuidesCrs =
true;
2526 QgsCoordinateTransform transform = QgsCoordinateTransform( mConstructionGuidesLayer->crs(), mMapCanvas->mapSettings().destinationCrs(),
QgsProject::instance()->
transformContext() );
2527 mConstructionGuidesLayer->setCrs( mMapCanvas->mapSettings().destinationCrs() );
2528 QgsFeatureIterator it = mConstructionGuidesLayer->getFeatures( QgsFeatureRequest().setNoAttributes() );
2532 QgsGeometry geom = feature.
geometry();
2534 mConstructionGuidesLayer->dataProvider()->changeGeometryValues( { { feature.
id(), geom } } );
2537 mDeferredUpdateConstructionGuidesCrs =
false;
2540void QgsAdvancedDigitizingDockWidget::resetConstructionGuides()
2542 if ( mConstructionGuidesLayer )
2544 mConstructionGuidesLayer.reset();
2547 const QgsVectorLayer::LayerOptions options(
QgsProject::instance()->transformContext(),
false,
false );
2548 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 (.
A widget that floats next to the mouse pointer, and allows interaction with the AdvancedDigitizing fe...
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.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
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...
Map canvas is a class for displaying all GIS data types on a canvas.
void destinationCrsChanged()
Emitted when map CRS has changed.
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
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.
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
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.
Shows a snapping marker on map canvas for the current snapping match.
void setTypeFlag(Qgis::SnappingTypes type)
define the type of snapping
void setMode(Qgis::SnappingMode mode)
define the mode of snapping
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 Q_INVOKABLE bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
static Q_INVOKABLE 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)
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.