44#include <QActionGroup>
45#include <QCoreApplication>
50#include "moc_qgsadvanceddigitizingdockwidget.cpp"
52using namespace Qt::StringLiterals;
54const QgsSettingsEntryBool *QgsAdvancedDigitizingDockWidget::settingsCadSnappingPriorityPrioritizeFeature
67 , mMapCanvas( canvas )
68 , mUserInputWidget( userInputWidget )
76 mAngleConstraint = std::make_unique<CadConstraint>( mAngleLineEdit, mLockAngleButton, mRelativeAngleButton, mRepeatingLockAngleButton );
78 mAngleConstraint->setMapCanvas( mMapCanvas );
79 mDistanceConstraint = std::make_unique<CadConstraint>( mDistanceLineEdit, mLockDistanceButton,
nullptr, mRepeatingLockDistanceButton );
81 mDistanceConstraint->setMapCanvas( mMapCanvas );
82 mXConstraint = std::make_unique<CadConstraint>( mXLineEdit, mLockXButton, mRelativeXButton, mRepeatingLockXButton );
84 mXConstraint->setMapCanvas( mMapCanvas );
85 mYConstraint = std::make_unique<CadConstraint>( mYLineEdit, mLockYButton, mRelativeYButton, mRepeatingLockYButton );
87 mYConstraint->setMapCanvas( mMapCanvas );
88 mZConstraint = std::make_unique<CadConstraint>( mZLineEdit, mLockZButton, mRelativeZButton, mRepeatingLockZButton );
90 mZConstraint->setMapCanvas( mMapCanvas );
91 mMConstraint = std::make_unique<CadConstraint>( mMLineEdit, mLockMButton, mRelativeMButton, mRepeatingLockMButton );
93 mMConstraint->setMapCanvas( mMapCanvas );
95 mLineExtensionConstraint = std::make_unique<CadConstraint>(
new QLineEdit(),
new QToolButton() );
96 mXyVertexConstraint = std::make_unique<CadConstraint>(
new QLineEdit(),
new QToolButton() );
97 mXyVertexConstraint->setMapCanvas( mMapCanvas );
101 mMapCanvas->installEventFilter(
this );
102 mAngleLineEdit->installEventFilter(
this );
103 mDistanceLineEdit->installEventFilter(
this );
104 mXLineEdit->installEventFilter(
this );
105 mYLineEdit->installEventFilter(
this );
106 mZLineEdit->installEventFilter(
this );
107 mMLineEdit->installEventFilter(
this );
110 connect( mEnableAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::activateCad );
111 connect( mConstructionModeAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::setConstructionMode );
112 connect( mParallelAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
113 connect( mPerpendicularAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
114 connect( mLockAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
115 connect( mLockDistanceButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
116 connect( mLockXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
117 connect( mLockYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
118 connect( mLockZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
119 connect( mLockMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
120 connect( mRelativeAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
121 connect( mRelativeXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
122 connect( mRelativeYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
123 connect( mRelativeZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
124 connect( mRelativeMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
125 connect( mRepeatingLockDistanceButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
126 connect( mRepeatingLockAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
127 connect( mRepeatingLockXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
128 connect( mRepeatingLockYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
129 connect( mRepeatingLockZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
130 connect( mRepeatingLockMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
131 connect( mAngleLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
132 connect( mDistanceLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
133 connect( mXLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
134 connect( mYLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
135 connect( mZLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
136 connect( mMLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
137 connect( mAngleLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
138 connect( mDistanceLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
139 connect( mXLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
140 connect( mYLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
141 connect( mZLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
142 connect( mMLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
148 whileBlocking( mAngleLineEdit )->setText( cleanedInputValue );
154 whileBlocking( mDistanceLineEdit )->setText( cleanedInputValue );
166 mCommonAngleActionsMenu =
new QMenu(
this );
168#ifndef __clang_analyzer__
169 QActionGroup *angleButtonGroup =
new QActionGroup( mCommonAngleActionsMenu );
171 QList<QPair<double, QString>> commonAngles;
172 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 } );
173 for ( QList<double>::const_iterator it = anglesDouble.constBegin(); it != anglesDouble.constEnd(); ++it )
179 QMenu *snappingPriorityMenu =
new QMenu( tr(
"Snapping Priority" ), mCommonAngleActionsMenu );
180 QActionGroup *snappingPriorityActionGroup =
new QActionGroup( snappingPriorityMenu );
181 QAction *featuresAction =
new QAction( tr(
"Prioritize Snapping to Features" ), snappingPriorityActionGroup );
182 featuresAction->setCheckable(
true );
183 QAction *anglesAction =
new QAction( tr(
"Prioritize Snapping to Common Angles" ), snappingPriorityActionGroup );
184 anglesAction->setCheckable(
true );
185 snappingPriorityActionGroup->addAction( featuresAction );
186 snappingPriorityActionGroup->addAction( anglesAction );
187 snappingPriorityMenu->addAction( anglesAction );
188 snappingPriorityMenu->addAction( featuresAction );
189 connect( anglesAction, &QAction::changed,
this, [
this, featuresAction] {
190 mSnappingPrioritizeFeatures = featuresAction->isChecked();
191 settingsCadSnappingPriorityPrioritizeFeature->setValue( featuresAction->isChecked() );
193 featuresAction->setChecked( settingsCadSnappingPriorityPrioritizeFeature->value() );
194 anglesAction->setChecked( !featuresAction->isChecked() );
195 mCommonAngleActionsMenu->addMenu( snappingPriorityMenu );
198 for ( QList<QPair<double, QString>>::const_iterator it = commonAngles.constBegin(); it != commonAngles.constEnd(); ++it )
200 QAction *action =
new QAction( it->second, mCommonAngleActionsMenu );
201 action->setCheckable(
true );
202 action->setChecked( it->first == mCommonAngleConstraint );
203 mCommonAngleActionsMenu->addAction( action );
205#ifndef __clang_analyzer__
206 angleButtonGroup->addAction( action );
208 mCommonAngleActions.insert( it->first, action );
212 QMenu *constructionSettingsMenu =
new QMenu(
this );
214 mRecordConstructionGuides =
new QAction( tr(
"Record Construction Guides" ), constructionSettingsMenu );
215 mRecordConstructionGuides->setCheckable(
true );
216 mRecordConstructionGuides->setChecked( settingsCadRecordConstructionGuides->value() );
217 constructionSettingsMenu->addAction( mRecordConstructionGuides );
218 connect( mRecordConstructionGuides, &QAction::triggered,
this, [
this]() { settingsCadRecordConstructionGuides->setValue( mRecordConstructionGuides->isChecked() ); } );
220 mShowConstructionGuides =
new QAction( tr(
"Show Construction Guides" ), constructionSettingsMenu );
221 mShowConstructionGuides->setCheckable(
true );
222 mShowConstructionGuides->setChecked( settingsCadShowConstructionGuides->value() );
223 constructionSettingsMenu->addAction( mShowConstructionGuides );
224 connect( mShowConstructionGuides, &QAction::triggered,
this, [
this]() {
225 settingsCadShowConstructionGuides->setValue( mShowConstructionGuides->isChecked() );
229 mSnapToConstructionGuides =
new QAction( tr(
"Snap to Visible Construction Guides" ), constructionSettingsMenu );
230 mSnapToConstructionGuides->setCheckable(
true );
231 mSnapToConstructionGuides->setChecked( settingsCadSnapToConstructionGuides->value() );
232 constructionSettingsMenu->addAction( mSnapToConstructionGuides );
233 connect( mSnapToConstructionGuides, &QAction::triggered,
this, [
this]() { settingsCadSnapToConstructionGuides->setValue( mSnapToConstructionGuides->isChecked() ); } );
235 constructionSettingsMenu->addSeparator();
237 mClearConstructionGuides =
new QAction( tr(
"Clear Construction Guides" ), constructionSettingsMenu );
238 constructionSettingsMenu->addAction( mClearConstructionGuides );
239 connect( mClearConstructionGuides, &QAction::triggered,
this, [
this]() {
240 resetConstructionGuides();
244 QToolButton *constructionModeToolButton = qobject_cast<QToolButton *>( mToolbar->widgetForAction( mConstructionModeAction ) );
245 constructionModeToolButton->setPopupMode( QToolButton::MenuButtonPopup );
246 constructionModeToolButton->setMenu( constructionSettingsMenu );
247 constructionModeToolButton->setObjectName( u
"ConstructionModeButton"_s );
250 QMenu *toolsMenu =
new QMenu(
this );
251 connect( toolsMenu, &QMenu::aboutToShow,
this, [
this, toolsMenu]() {
254 for (
const QString &name : toolMetadataNames )
257 QAction *toolAction =
new QAction( toolMetadata->
icon(), toolMetadata->
visibleName(), toolsMenu );
258 connect( toolAction, &QAction::triggered,
this, [
this, toolMetadata]() {
setTool( toolMetadata->
createTool( mMapCanvas,
this ) ); } );
259 toolsMenu->addAction( toolAction );
262 qobject_cast<QToolButton *>( mToolbar->widgetForAction( mToolsAction ) )->setPopupMode( QToolButton::InstantPopup );
263 mToolsAction->setMenu( toolsMenu );
265 qobject_cast<QToolButton *>( mToolbar->widgetForAction( mSettingsAction ) )->setPopupMode( QToolButton::InstantPopup );
266 mSettingsAction->setMenu( mCommonAngleActionsMenu );
267 mSettingsAction->setCheckable(
true );
268 mSettingsAction->setToolTip(
"<b>" + tr(
"Snap to common angles" ) +
"</b><br>(" + tr(
"press n to cycle through the options" ) +
")" );
269 mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
270 connect( mCommonAngleActionsMenu, &QMenu::triggered,
this, &QgsAdvancedDigitizingDockWidget::settingsButtonTriggered );
273 QMenu *constructionMenu =
new QMenu(
this );
275 mLineExtensionAction =
new QAction( tr(
"Line Extension" ), constructionMenu );
276 mLineExtensionAction->setCheckable(
true );
277 constructionMenu->addAction( mLineExtensionAction );
278 connect( mLineExtensionAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
280 mXyVertexAction =
new QAction( tr(
"X/Y Point" ), constructionMenu );
281 mXyVertexAction->setCheckable(
true );
282 constructionMenu->addAction( mXyVertexAction );
283 connect( mXyVertexAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
285 auto constructionToolBar = qobject_cast<QToolButton *>( mToolbar->widgetForAction( mConstructionAction ) );
286 constructionToolBar->setPopupMode( QToolButton::InstantPopup );
287 mConstructionAction->setMenu( constructionMenu );
288 constructionToolBar->setObjectName( u
"ConstructionButton"_s );
289 mConstructionAction->setCheckable(
true );
290 mConstructionAction->setToolTip( tr(
"Construction Tools" ) );
293 mConstructionModeAction->setToolTip(
"<b>" + tr(
"Construction mode" ) +
"</b><br>(" + tr(
"press c to toggle on/off" ) +
")" );
294 mDistanceLineEdit->setToolTip(
"<b>" + tr(
"Distance" ) +
"</b><br>(" + tr(
"press d for quick access" ) +
")" );
295 mLockDistanceButton->setToolTip(
"<b>" + tr(
"Lock distance" ) +
"</b><br>(" + tr(
"press Ctrl + d for quick access" ) +
")" );
296 mRepeatingLockDistanceButton->setToolTip(
"<b>" + tr(
"Continuously lock distance" ) +
"</b>" );
298 mRelativeAngleButton->setToolTip(
"<b>" + tr(
"Toggles relative angle to previous segment" ) +
"</b><br>(" + tr(
"press Shift + a for quick access" ) +
")" );
299 mAngleLineEdit->setToolTip(
"<b>" + tr(
"Angle" ) +
"</b><br>(" + tr(
"press a for quick access" ) +
")" );
300 mLockAngleButton->setToolTip(
"<b>" + tr(
"Lock angle" ) +
"</b><br>(" + tr(
"press Ctrl + a for quick access" ) +
")" );
301 mRepeatingLockAngleButton->setToolTip(
"<b>" + tr(
"Continuously lock angle" ) +
"</b>" );
303 mRelativeXButton->setToolTip(
"<b>" + tr(
"Toggles relative x to previous node" ) +
"</b><br>(" + tr(
"press Shift + x for quick access" ) +
")" );
304 mXLineEdit->setToolTip(
"<b>" + tr(
"X coordinate" ) +
"</b><br>(" + tr(
"press x for quick access" ) +
")" );
305 mLockXButton->setToolTip(
"<b>" + tr(
"Lock x coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + x for quick access" ) +
")" );
306 mRepeatingLockXButton->setToolTip(
"<b>" + tr(
"Continuously lock x coordinate" ) +
"</b>" );
308 mRelativeYButton->setToolTip(
"<b>" + tr(
"Toggles relative y to previous node" ) +
"</b><br>(" + tr(
"press Shift + y for quick access" ) +
")" );
309 mYLineEdit->setToolTip(
"<b>" + tr(
"Y coordinate" ) +
"</b><br>(" + tr(
"press y for quick access" ) +
")" );
310 mLockYButton->setToolTip(
"<b>" + tr(
"Lock y coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + y for quick access" ) +
")" );
311 mRepeatingLockYButton->setToolTip(
"<b>" + tr(
"Continuously lock y coordinate" ) +
"</b>" );
313 mRelativeZButton->setToolTip(
"<b>" + tr(
"Toggles relative z to previous node" ) +
"</b><br>(" + tr(
"press Shift + z for quick access" ) +
")" );
314 mZLineEdit->setToolTip(
"<b>" + tr(
"Z coordinate" ) +
"</b><br>(" + tr(
"press z for quick access" ) +
")" );
315 mLockZButton->setToolTip(
"<b>" + tr(
"Lock z coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + z for quick access" ) +
")" );
316 mRepeatingLockZButton->setToolTip(
"<b>" + tr(
"Continuously lock z coordinate" ) +
"</b>" );
318 mRelativeMButton->setToolTip(
"<b>" + tr(
"Toggles relative m to previous node" ) +
"</b><br>(" + tr(
"press Shift + m for quick access" ) +
")" );
319 mMLineEdit->setToolTip(
"<b>" + tr(
"M coordinate" ) +
"</b><br>(" + tr(
"press m for quick access" ) +
")" );
320 mLockMButton->setToolTip(
"<b>" + tr(
"Lock m coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + m for quick access" ) +
")" );
321 mRepeatingLockMButton->setToolTip(
"<b>" + tr(
"Continuously lock m coordinate" ) +
"</b>" );
332 mFloaterActionsMenu =
new QMenu(
this );
333 qobject_cast<QToolButton *>( mToolbar->widgetForAction( mFloaterAction ) )->setPopupMode( QToolButton::InstantPopup );
334 mFloaterAction->setMenu( mFloaterActionsMenu );
335 mFloaterAction->setCheckable(
true );
337 mFloaterAction->setChecked( mFloater->active() );
341 QAction *action =
new QAction( tr(
"Show Floater" ), mFloaterActionsMenu );
342 action->setCheckable(
true );
343 action->setChecked( mFloater->active() );
344 mFloaterActionsMenu->addAction( action );
345 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
346 mFloater->setActive( checked );
347 mFloaterAction->setChecked( checked );
351 mFloaterActionsMenu->addSeparator();
354 QAction *action =
new QAction( tr(
"Show Distance" ), mFloaterActionsMenu );
355 action->setCheckable(
true );
356 mFloaterActionsMenu->addAction( action );
358 const bool isDistanceChecked =
QgsSettings().
value( u
"/Cad/DistanceShowInFloater"_s,
true ).toBool();
359 action->setChecked( isDistanceChecked );
365 QAction *action =
new QAction( tr(
"Show Angle" ), mFloaterActionsMenu );
366 action->setCheckable(
true );
367 mFloaterActionsMenu->addAction( action );
369 const bool isAngleChecked =
QgsSettings().
value( u
"/Cad/AngleShowInFloater"_s,
true ).toBool();
370 action->setChecked( isAngleChecked );
376 QAction *action =
new QAction( tr(
"Show XY Coordinates" ), mFloaterActionsMenu );
377 action->setCheckable(
true );
378 mFloaterActionsMenu->addAction( action );
379 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
384 const bool isXCoordinateChecked =
QgsSettings().
value( u
"/Cad/XCoordinateShowInFloater"_s,
true ).toBool();
385 action->setChecked( isXCoordinateChecked );
393 QAction *action =
new QAction( tr(
"Show Z Value" ), mFloaterActionsMenu );
394 action->setCheckable(
true );
395 mFloaterActionsMenu->addAction( action );
397 const bool isZCoordinateChecked =
QgsSettings().
value( u
"/Cad/ZCoordinateShowInFloater"_s,
true ).toBool();
398 action->setChecked( isZCoordinateChecked );
405 QAction *action =
new QAction( tr(
"Show M Value" ), mFloaterActionsMenu );
406 action->setCheckable(
true );
407 mFloaterActionsMenu->addAction( action );
409 const bool isMCoordinateChecked =
QgsSettings().
value( u
"/Cad/MCoordinateShowInFloater"_s,
true ).toBool();
410 action->setChecked( isMCoordinateChecked );
417 QAction *action =
new QAction( tr(
"Show Bearing/Azimuth" ), mFloaterActionsMenu );
418 action->setCheckable(
true );
419 mFloaterActionsMenu->addAction( action );
421 const bool isBearingChecked =
QgsSettings().
value( u
"/Cad/BearingShowInFloater"_s,
false ).toBool();
422 action->setChecked( isBearingChecked );
429 QAction *action =
new QAction( tr(
"Show Common Snapping Angle" ), mFloaterActionsMenu );
430 action->setCheckable(
true );
431 mFloaterActionsMenu->addAction( action );
433 const bool isCommonAngleSnappingChecked =
QgsSettings().
value( u
"/Cad/CommonAngleSnappingShowInFloater"_s,
false ).toBool();
434 action->setChecked( isCommonAngleSnappingChecked );
441 QMenu *menu =
new QMenu( tr(
"Show Area" ), mFloaterActionsMenu );
442 mFloaterActionsMenu->addMenu( menu );
444 QAction *actionHidden =
new QAction( tr(
"Hidden" ), mFloaterActionsMenu );
446 actionHidden->setCheckable(
true );
447 menu->addAction( actionHidden );
449 QAction *actionEllipsoidal =
new QAction( tr(
"Show Ellipsoidal Area" ), mFloaterActionsMenu );
451 actionEllipsoidal->setCheckable(
true );
452 menu->addAction( actionEllipsoidal );
454 QAction *actionCartesian =
new QAction( tr(
"Show Cartesian Area" ), mFloaterActionsMenu );
456 actionCartesian->setCheckable(
true );
457 menu->addAction( actionCartesian );
459 QActionGroup *group =
new QActionGroup( menu );
460 group->addAction( actionHidden );
461 group->addAction( actionEllipsoidal );
462 group->addAction( actionCartesian );
465 for ( QAction *action : group->actions() )
469 action->setChecked(
true );
471 connect( action, &QAction::toggled,
this, [
this, action](
bool checked ) {
482 QMenu *menu =
new QMenu( tr(
"Show Total Length/Perimeter" ), mFloaterActionsMenu );
483 mFloaterActionsMenu->addMenu( menu );
485 QAction *actionHidden =
new QAction( tr(
"Hidden" ), mFloaterActionsMenu );
487 actionHidden->setCheckable(
true );
488 menu->addAction( actionHidden );
490 QAction *actionEllipsoidal =
new QAction( tr(
"Show Ellipsoidal Lengths" ), mFloaterActionsMenu );
492 actionEllipsoidal->setCheckable(
true );
493 menu->addAction( actionEllipsoidal );
495 QAction *actionCartesian =
new QAction( tr(
"Show Cartesian Lengths" ), mFloaterActionsMenu );
497 actionCartesian->setCheckable(
true );
498 menu->addAction( actionCartesian );
500 QActionGroup *group =
new QActionGroup( menu );
501 group->addAction( actionHidden );
502 group->addAction( actionEllipsoidal );
503 group->addAction( actionCartesian );
506 for ( QAction *action : group->actions() )
510 action->setChecked(
true );
512 connect( action, &QAction::toggled,
this, [
this, action](
bool checked ) {
523 QAction *action =
new QAction( tr(
"Show Weight" ), mFloaterActionsMenu );
524 action->setCheckable(
true );
525 mFloaterActionsMenu->addAction( action );
527 const bool isWeightChecked =
QgsSettings().
value( u
"/Cad/WeightShowInFloater"_s,
true ).toBool();
528 action->setChecked( isWeightChecked );
533 updateCapacity(
true );
546 mCurrentTool->deleteLater();
553 return tr(
"Do Not Snap to Common Angles" );
555 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 );
574 QString totalLengthString;
577 mMapCanvas->mapSettings().destinationCrs(),
591 mXLineEdit->setText( value );
594 emit mXLineEdit->returnPressed();
598 QEvent *e =
new QEvent( QEvent::FocusOut );
599 QCoreApplication::postEvent( mXLineEdit, e );
603 emit mXLineEdit->textEdited( value );
608 mYLineEdit->setText( value );
611 emit mYLineEdit->returnPressed();
615 QEvent *e =
new QEvent( QEvent::FocusOut );
616 QCoreApplication::postEvent( mYLineEdit, e );
620 emit mYLineEdit->textEdited( value );
625 mZLineEdit->setText( value );
628 emit mZLineEdit->returnPressed();
632 QEvent *e =
new QEvent( QEvent::FocusOut );
633 QCoreApplication::postEvent( mZLineEdit, e );
637 emit mZLineEdit->textEdited( value );
642 mMLineEdit->setText( value );
645 emit mMLineEdit->returnPressed();
649 QEvent *e =
new QEvent( QEvent::FocusOut );
650 QCoreApplication::postEvent( mMLineEdit, e );
654 emit mMLineEdit->textEdited( value );
659 mAngleLineEdit->setText( value );
662 emit mAngleLineEdit->returnPressed();
666 emit mAngleLineEdit->textEdited( value );
671 mDistanceLineEdit->setText( value );
674 emit mDistanceLineEdit->returnPressed();
678 QEvent *e =
new QEvent( QEvent::FocusOut );
679 QCoreApplication::postEvent( mDistanceLineEdit, e );
683 emit mDistanceLineEdit->textEdited( value );
688void QgsAdvancedDigitizingDockWidget::setCadEnabled(
bool enabled )
690 mCadEnabled = enabled;
691 mEnableAction->setChecked( enabled );
692 mConstructionModeAction->setEnabled( enabled );
693 mSettingsAction->setEnabled( enabled );
694 mInputWidgets->setEnabled( enabled );
695 mFloaterAction->setEnabled( enabled );
696 mConstructionAction->setEnabled( enabled );
697 mToolsAction->setEnabled( enabled );
702 mLineExtensionAction->setChecked(
false );
703 mXyVertexAction->setChecked(
false );
705 mParallelAction->setEnabled(
false );
706 mPerpendicularAction->setEnabled(
false );
709 mCurrentTool->deleteLater();
716 setConstructionMode(
false );
726 mLastSnapMatch = QgsPointLocator::Match();
732 bool enableZ =
false;
733 bool enableM =
false;
737 switch ( layer->type() )
750 QgsMeshLayer *mlayer = qobject_cast<QgsMeshLayer *>( layer );
766 mTargetLayerSupportsM = enableM;
767 mTargetLayerSupportsZ = enableZ;
775 if (
enable && !mTargetLayerSupportsZ )
779 mRelativeZButton->setEnabled(
enable );
780 mZLabel->setEnabled(
enable );
781 mZLineEdit->setEnabled(
enable );
782 if ( mZLineEdit->isEnabled() )
786 mLockZButton->setEnabled(
enable );
792 if (
enable && !mTargetLayerSupportsM )
796 mRelativeMButton->setEnabled(
enable );
797 mMLabel->setEnabled(
enable );
798 mMLineEdit->setEnabled(
enable );
799 if ( mMLineEdit->isEnabled() )
803 mLockMButton->setEnabled(
enable );
807void QgsAdvancedDigitizingDockWidget::activateCad(
bool enabled )
809 enabled &= mCurrentMapToolSupportsCad;
811 mSessionActive = enabled;
813 if ( enabled && !isVisible() )
818 setCadEnabled( enabled );
825 mCurrentTool->deleteLater();
826 mCurrentTool =
nullptr;
833 if ( QWidget *toolWidget = mCurrentTool->createWidget() )
835 toolWidget->setParent( mUserInputWidget );
836 mUserInputWidget->addUserInputWidget( toolWidget );
844 return mCurrentTool.data();
847void QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked(
bool activated )
853 else if ( sender() == mParallelAction )
857 else if ( sender() == mPerpendicularAction )
863void QgsAdvancedDigitizingDockWidget::setConstraintRelative(
bool activate )
865 if ( sender() == mRelativeAngleButton )
867 mAngleConstraint->setRelative( activate );
870 else if ( sender() == mRelativeXButton )
872 mXConstraint->setRelative( activate );
875 else if ( sender() == mRelativeYButton )
877 mYConstraint->setRelative( activate );
880 else if ( sender() == mRelativeZButton )
882 mZConstraint->setRelative( activate );
885 else if ( sender() == mRelativeMButton )
887 mMConstraint->setRelative( activate );
892void QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock(
bool activate )
894 if ( sender() == mRepeatingLockDistanceButton )
896 mDistanceConstraint->setRepeatingLock( activate );
898 else if ( sender() == mRepeatingLockAngleButton )
900 mAngleConstraint->setRepeatingLock( activate );
902 else if ( sender() == mRepeatingLockXButton )
904 mXConstraint->setRepeatingLock( activate );
906 else if ( sender() == mRepeatingLockYButton )
908 mYConstraint->setRepeatingLock( activate );
910 else if ( sender() == mRepeatingLockZButton )
912 mZConstraint->setRepeatingLock( activate );
914 else if ( sender() == mRepeatingLockMButton )
916 mMConstraint->setRepeatingLock( activate );
920void QgsAdvancedDigitizingDockWidget::setConstructionMode(
bool enabled )
922 mConstructionMode = enabled;
923 mConstructionModeAction->setChecked( enabled );
927 if ( enabled && mCadPointList.size() > 1 )
929 mConstructionGuideLine.addVertex( mCadPointList.at( 1 ) );
934void QgsAdvancedDigitizingDockWidget::settingsButtonTriggered( QAction *action )
937 for (
auto it = mCommonAngleActions.cbegin(); it != mCommonAngleActions.cend(); ++it )
939 if ( it.value() == action )
941 it.value()->setChecked(
true );
942 mCommonAngleConstraint = it.key();
944 mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
951QgsMapLayer *QgsAdvancedDigitizingDockWidget::targetLayer()
const
953 if ( QgsMapToolAdvancedDigitizing *advancedTool = qobject_cast<QgsMapToolAdvancedDigitizing *>( mMapCanvas->mapTool() ) )
955 return advancedTool->layer();
959 return mMapCanvas->currentLayer();
969 if ( releaseRepeatingLocks )
971 mXyVertexAction->setChecked(
false );
975 mLineExtensionAction->setChecked(
false );
982 if ( releaseRepeatingLocks || !mAngleConstraint->isRepeatingLock() )
987 if ( releaseRepeatingLocks || !mDistanceConstraint->isRepeatingLock() )
992 if ( releaseRepeatingLocks || !mXConstraint->isRepeatingLock() )
997 if ( releaseRepeatingLocks || !mYConstraint->isRepeatingLock() )
1002 if ( releaseRepeatingLocks || !mZConstraint->isRepeatingLock() )
1007 if ( releaseRepeatingLocks || !mMConstraint->isRepeatingLock() )
1013 if ( !mCadPointList.empty() )
1015 if ( !mXConstraint->isLocked() && !mXConstraint->relative() )
1017 mXConstraint->setValue( mCadPointList.constLast().x(),
true );
1019 if ( !mYConstraint->isLocked() && !mYConstraint->relative() )
1021 mYConstraint->setValue( mCadPointList.constLast().y(),
true );
1023 if ( !mZConstraint->isLocked() && !mZConstraint->relative() )
1025 mZConstraint->setValue( mCadPointList.constLast().z(),
true );
1027 if ( !mMConstraint->isLocked() && !mMConstraint->relative() )
1029 mMConstraint->setValue( mCadPointList.constLast().m(),
true );
1035void QgsAdvancedDigitizingDockWidget::emit pointChanged()
1038 QPoint globalPos = mMapCanvas->cursor().pos();
1039 QPoint pos = mMapCanvas->mapFromGlobal( globalPos );
1040 QMouseEvent *e =
new QMouseEvent( QEvent::MouseMove, pos, globalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier );
1041 mCurrentMapTool->canvasMoveEvent( e );
1048 if ( obj == mAngleLineEdit || obj == mLockAngleButton )
1050 constraint = mAngleConstraint.get();
1052 else if ( obj == mDistanceLineEdit || obj == mLockDistanceButton )
1054 constraint = mDistanceConstraint.get();
1056 else if ( obj == mXLineEdit || obj == mLockXButton )
1058 constraint = mXConstraint.get();
1060 else if ( obj == mYLineEdit || obj == mLockYButton )
1062 constraint = mYConstraint.get();
1064 else if ( obj == mZLineEdit || obj == mLockZButton )
1066 constraint = mZConstraint.get();
1068 else if ( obj == mMLineEdit || obj == mLockMButton )
1070 constraint = mMConstraint.get();
1072 else if ( obj == mLineExtensionAction )
1074 constraint = mLineExtensionConstraint.get();
1076 else if ( obj == mXyVertexAction )
1078 constraint = mXyVertexConstraint.get();
1083double QgsAdvancedDigitizingDockWidget::parseUserInput(
const QString &inputValue,
const Qgis::CadConstraintType type,
bool &ok )
const
1093 QgsExpression expr( inputValue );
1094 const QVariant result = expr.evaluate();
1095 if ( expr.hasEvalError() )
1098 QString inputValueC { inputValue };
1101 if ( inputValue.contains( QLocale().groupSeparator() ) )
1103 inputValueC.remove( QLocale().groupSeparator() );
1104 QgsExpression exprC( inputValueC );
1105 const QVariant resultC = exprC.evaluate();
1106 if ( !exprC.hasEvalError() )
1108 value = resultC.toDouble( &ok );
1113 if ( !ok && QLocale().decimalPoint() != QChar(
'.' ) && inputValueC.contains( QLocale().decimalPoint() ) )
1115 QgsExpression exprC( inputValueC.replace( QLocale().decimalPoint(), QChar(
'.' ) ) );
1116 const QVariant resultC = exprC.evaluate();
1117 if ( !exprC.hasEvalError() )
1119 value = resultC.toDouble( &ok );
1125 value = result.toDouble( &ok );
1140void QgsAdvancedDigitizingDockWidget::updateConstraintValue( CadConstraint *constraint,
const QString &textValue,
bool convertExpression )
1142 if ( !constraint || textValue.isEmpty() )
1151 const double value = parseUserInput( textValue, constraint->cadConstraintType(), ok );
1155 constraint->setValue( value, convertExpression );
1160void QgsAdvancedDigitizingDockWidget::lockConstraint(
bool activate )
1170 const QString textValue = constraint->lineEdit()->text();
1171 if ( !textValue.isEmpty() )
1174 const double value = parseUserInput( textValue, constraint->cadConstraintType(), ok );
1177 constraint->setValue( value );
1191 if ( constraint == mXConstraint.get() )
1195 else if ( constraint == mYConstraint.get() )
1199 else if ( constraint == mZConstraint.get() )
1203 else if ( constraint == mMConstraint.get() )
1207 else if ( constraint == mDistanceConstraint.get() )
1211 else if ( constraint == mAngleConstraint.get() )
1219 if ( constraint == mAngleConstraint.get() )
1229void QgsAdvancedDigitizingDockWidget::constraintTextEdited(
const QString &textValue )
1237 updateConstraintValue( constraint, textValue,
false );
1240void QgsAdvancedDigitizingDockWidget::constraintFocusOut()
1242 QLineEdit *lineEdit = qobject_cast<QLineEdit *>( sender()->parent() );
1252 updateConstraintValue( constraint, lineEdit->text(),
true );
1257 mBetweenLineConstraint = constraint;
1262void QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint(
bool activate )
1272 if ( constraint == mXyVertexConstraint.get() )
1276 else if ( constraint == mLineExtensionConstraint.get() )
1290void QgsAdvancedDigitizingDockWidget::updateCapacity(
bool updateUIwithoutChange )
1293 const bool isGeographic = mMapCanvas->mapSettings().destinationCrs().isGeographic();
1296 if ( mCadPointList.count() > 1 )
1299 if ( !isGeographic )
1305 if ( mCadPointList.count() > 2 )
1307 if ( !isGeographic )
1310 if ( !updateUIwithoutChange && newCapacities == mCapacities )
1320 const bool distance = mCadEnabled && newCapacities.testFlag(
Distance );
1321 const bool relativeAngle = mCadEnabled && newCapacities.testFlag(
RelativeAngle );
1322 const bool absoluteAngle = mCadEnabled && newCapacities.testFlag(
AbsoluteAngle );
1323 const bool relativeCoordinates = mCadEnabled && newCapacities.testFlag(
RelativeCoordinates );
1325 mPerpendicularAction->setEnabled( distance && snappingEnabled );
1326 mParallelAction->setEnabled( distance && snappingEnabled );
1328 mLineExtensionAction->setEnabled( snappingEnabled );
1329 mXyVertexAction->setEnabled( snappingEnabled );
1333 if ( !snappingEnabled )
1335 mPerpendicularAction->setToolTip( tr(
"Snapping must be enabled to utilize perpendicular mode." ) );
1336 mParallelAction->setToolTip( tr(
"Snapping must be enabled to utilize parallel mode." ) );
1337 mLineExtensionAction->setToolTip( tr(
"Snapping must be enabled to utilize line extension mode." ) );
1338 mXyVertexAction->setToolTip( tr(
"Snapping must be enabled to utilize xy point mode." ) );
1340 else if ( mCadPointList.count() <= 1 )
1342 mPerpendicularAction->setToolTip( tr(
"A first vertex should be drawn to utilize perpendicular mode." ) );
1343 mParallelAction->setToolTip( tr(
"A first vertex should be drawn to utilize parallel mode." ) );
1345 else if ( isGeographic )
1347 mPerpendicularAction->setToolTip( tr(
"Perpendicular mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1348 mParallelAction->setToolTip( tr(
"Parallel mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1352 mPerpendicularAction->setToolTip(
"<b>" + tr(
"Perpendicular" ) +
"</b><br>(" + tr(
"press p to switch between perpendicular, parallel and normal mode" ) +
")" );
1353 mParallelAction->setToolTip(
"<b>" + tr(
"Parallel" ) +
"</b><br>(" + tr(
"press p to switch between perpendicular, parallel and normal mode" ) +
")" );
1357 if ( !absoluteAngle )
1363 mLockAngleButton->setEnabled( absoluteAngle );
1364 mRelativeAngleButton->setEnabled( relativeAngle );
1365 mAngleLineEdit->setEnabled( absoluteAngle );
1367 if ( !absoluteAngle )
1371 if ( !relativeAngle )
1373 mAngleConstraint->setRelative(
false );
1376 else if ( relativeAngle && !mCapacities.testFlag(
RelativeAngle ) )
1379 mAngleConstraint->setRelative(
true );
1384 mLockDistanceButton->setEnabled( distance && relativeCoordinates );
1385 mDistanceLineEdit->setEnabled( distance && relativeCoordinates );
1387 if ( !( distance && relativeCoordinates ) )
1392 mRelativeXButton->setEnabled( relativeCoordinates );
1393 mRelativeYButton->setEnabled( relativeCoordinates );
1394 mRelativeZButton->setEnabled( relativeCoordinates );
1395 mRelativeMButton->setEnabled( relativeCoordinates );
1398 mCapacities = newCapacities;
1399 mCadPaintItem->updatePosition();
1406 constr.
locked =
c->isLocked();
1408 constr.
value =
c->value();
1415 if ( !mLineExtensionConstraint->isLocked() && !mXyVertexConstraint->isLocked() )
1421 const int lastIndex = mLockedSnapVertices.length() - 1;
1422 for (
int i = lastIndex; i >= 0; --i )
1424 if ( mLockedSnapVertices[i].point() == snapMatch.
point() )
1426 if ( snapMatch.
point() != previouslySnap.
point() )
1428 mLockedSnapVertices.removeAt( i );
1434 if ( snapMatch.
point() != previouslySnap.
point() )
1436 mLockedSnapVertices.enqueue( snapMatch );
1439 if ( mLockedSnapVertices.count() > 3 )
1441 mLockedSnapVertices.dequeue();
1450 context.
xConstraint = _constraint( mXConstraint.get() );
1451 context.
yConstraint = _constraint( mYConstraint.get() );
1452 context.
zConstraint = _constraint( mZConstraint.get() );
1453 context.
mConstraint = _constraint( mMConstraint.get() );
1480 const bool res = output.
valid;
1482 mSnappedSegment.clear();
1487 mSnappedSegment << edgePt0 << edgePt1;
1508 mSnapIndicator->setMatch( output.
snapMatch );
1509 mSnapIndicator->setVisible(
true );
1513 mSnapIndicator->setVisible(
false );
1535 if ( mSnapMatch.layer() )
1539 if ( ( ( mSnapMatch.hasVertex() || mSnapMatch.hasLineEndpoint() ) && (
QgsPointXY( point ) == mSnapMatch.point() ) ) || ( mSnapMatch.hasEdge() &&
QgsProject::instance()->topologicalEditing() ) )
1542 point = mSnapMatch.interpolatedPoint( mMapCanvas->mapSettings().destinationCrs() );
1548 if ( mSnapMatch.hasVertex() || mSnapMatch.hasLineEndpoint() )
1550 toggleLockedSnapVertex( mSnapMatch, mLastSnapMatch );
1551 mLastSnapMatch = mSnapMatch;
1561 if ( mLockZButton->isChecked() )
1563 point.
setZ( QLocale().toDouble( mZLineEdit->text() ) );
1565 if ( mLockMButton->isChecked() )
1567 point.
setM( QLocale().toDouble( mMLineEdit->text() ) );
1573 updateUnlockedConstraintValues( point );
1581 emit
pushWarning( tr(
"Some constraints are incompatible. Resulting point might be incorrect." ) );
1588void QgsAdvancedDigitizingDockWidget::updateUnlockedConstraintValues(
const QgsPoint &point )
1590 bool previousPointExist, penulPointExist;
1595 if ( previousPointExist )
1597 double prevAngle = 0.0;
1599 if ( penulPointExist && mAngleConstraint->relative() )
1602 prevAngle = std::atan2( previousPt.
y() - penultimatePt.
y(), previousPt.
x() - penultimatePt.
x() ) * 180 / M_PI;
1605 const double xAngle { std::atan2( point.
y() - previousPt.
y(), point.
x() - previousPt.
x() ) * 180 / M_PI };
1608 const double angle = std::fmod( xAngle - prevAngle, 360.0 );
1609 if ( !mAngleConstraint->isLocked() )
1611 mAngleConstraint->setValue( angle );
1615 double bearing { std::fmod( xAngle, 360.0 ) };
1616 bearing = bearing <= 90.0 ? 90.0 - bearing : ( bearing > 90 ? 270.0 + 180.0 - bearing : 270.0 - bearing );
1617 const QgsNumericFormatContext context;
1622 if ( !mDistanceConstraint->isLocked() && previousPointExist )
1624 mDistanceConstraint->setValue( std::sqrt( previousPt.
distanceSquared( point ) ) );
1627 if ( !mXConstraint->isLocked() )
1629 if ( previousPointExist && mXConstraint->relative() )
1631 mXConstraint->setValue( point.
x() - previousPt.
x() );
1635 mXConstraint->setValue( point.
x() );
1639 if ( !mYConstraint->isLocked() )
1641 if ( previousPointExist && mYConstraint->relative() )
1643 mYConstraint->setValue( point.
y() - previousPt.
y() );
1647 mYConstraint->setValue( point.
y() );
1651 if ( !mZConstraint->isLocked() )
1653 if ( previousPointExist && mZConstraint->relative() )
1655 mZConstraint->setValue( point.
z() - previousPt.
z() );
1659 mZConstraint->setValue( point.
z() );
1663 if ( !mMConstraint->isLocked() )
1665 if ( previousPointExist && mMConstraint->relative() )
1667 mMConstraint->setValue( point.
m() - previousPt.
m() );
1671 mMConstraint->setValue( point.
m() );
1677QList<QgsPointXY> QgsAdvancedDigitizingDockWidget::snapSegmentToAllLayers(
const QgsPointXY &originalMapPoint,
bool *snapped )
const
1680 QgsPointXY pt1, pt2;
1681 QgsPointLocator::Match match;
1683 QgsSnappingUtils *snappingUtils = mMapCanvas->snappingUtils();
1685 const QgsSnappingConfig canvasConfig = snappingUtils->
config();
1686 QgsSnappingConfig localConfig = snappingUtils->
config();
1690 snappingUtils->
setConfig( localConfig );
1692 match = snappingUtils->
snapToMap( originalMapPoint,
nullptr,
true );
1694 snappingUtils->
setConfig( canvasConfig );
1704 *snapped =
segment.count() == 2;
1714 mCurrentTool->canvasPressEvent( event );
1719 event->setAccepted(
false );
1731 mCurrentTool->canvasMoveEvent( event );
1739 if ( event->button() == Qt::RightButton )
1743 mCurrentTool->canvasReleaseEvent( event );
1744 if ( !event->isAccepted() )
1756 event->setAccepted(
false );
1762 mCurrentTool->canvasReleaseEvent( event );
1763 if ( !event->isAccepted() )
1774 if ( mLockZButton->isChecked() )
1776 point.
setZ( QLocale().toDouble( mZLineEdit->text() ) );
1778 if ( mLockMButton->isChecked() )
1780 point.
setM( QLocale().toDouble( mMLineEdit->text() ) );
1791 event->setAccepted(
false );
1803 bool previousPointExist, penulPointExist, snappedSegmentExist;
1806 mSnappedSegment = snapSegmentToAllLayers( e->
originalMapPoint(), &snappedSegmentExist );
1808 if ( !previousPointExist || !snappedSegmentExist )
1813 double angle = std::atan2( mSnappedSegment[0].y() - mSnappedSegment[1].y(), mSnappedSegment[0].x() - mSnappedSegment[1].x() );
1815 if ( mAngleConstraint->relative() && penulPointExist )
1817 angle -= std::atan2( previousPt.
y() - penultimatePt.
y(), previousPt.
x() - penultimatePt.
x() );
1825 angle *= 180 / M_PI;
1827 mAngleConstraint->setValue( angle );
1828 mAngleConstraint->setLockMode( lockMode );
1846 case Qt::Key_Backspace:
1847 case Qt::Key_Delete:
1853 case Qt::Key_Escape:
1872 mCurrentTool->deleteLater();
1875 if ( !mConstructionGuideLine.isEmpty() )
1877 mConstructionGuideLine.clear();
1893 case Qt::Key_Backspace:
1894 case Qt::Key_Delete:
1900 case Qt::Key_Escape:
1904 if ( mConstructionGuideLine.numPoints() >= 2 )
1906 mConstructionGuidesLayer->dataProvider()->deleteFeatures(
QgsFeatureIds() << mConstructionGuideId );
1907 mConstructionGuideLine.clear();
1912 mCurrentTool->deleteLater();
1919 filterKeyPress( e );
1928 const auto constPoints = points;
1937 mDistanceConstraint->toggleLocked();
1942bool QgsAdvancedDigitizingDockWidget::eventFilter( QObject *obj, QEvent *event )
1946 return QgsDockWidget::eventFilter( obj, event );
1954 if ( event->type() == QEvent::ShortcutOverride || event->type() == QEvent::KeyPress )
1956 if ( QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( event ) )
1958 return filterKeyPress( keyEvent );
1961 return QgsDockWidget::eventFilter( obj, event );
1964bool QgsAdvancedDigitizingDockWidget::filterKeyPress( QKeyEvent *e )
1970 const QEvent::Type type = e->type();
1973 case Qt::Key_Escape:
1975 if ( type == QEvent::KeyPress && mCurrentTool )
1977 mCurrentTool->deleteLater();
1979 else if ( type == QEvent::KeyPress && mConstructionMode && mConstructionGuideLine.numPoints() >= 2 )
1981 mConstructionGuidesLayer->dataProvider()->deleteFeatures(
QgsFeatureIds() << mConstructionGuideId );
1982 mConstructionGuideLine.clear();
1984 if ( mCadPointList.size() > 1 )
1986 mConstructionGuideLine.addVertex( mCadPointList.at( 1 ) );
2001 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
2003 mXConstraint->toggleLocked();
2008 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
2012 mXConstraint->toggleRelative();
2019 else if ( type == QEvent::KeyPress )
2021 mXLineEdit->setFocus();
2022 mXLineEdit->selectAll();
2031 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
2033 mYConstraint->toggleLocked();
2038 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
2042 mYConstraint->toggleRelative();
2049 else if ( type == QEvent::KeyPress )
2051 mYLineEdit->setFocus();
2052 mYLineEdit->selectAll();
2061 if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::AltModifier )
2063 mZConstraint->toggleLocked();
2068 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
2072 mZConstraint->toggleRelative();
2079 else if ( type == QEvent::KeyPress )
2081 mZLineEdit->setFocus();
2082 mZLineEdit->selectAll();
2091 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
2093 mMConstraint->toggleLocked();
2098 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
2102 mMConstraint->toggleRelative();
2109 else if ( type == QEvent::KeyPress )
2111 mMLineEdit->setFocus();
2112 mMLineEdit->selectAll();
2121 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
2125 mAngleConstraint->toggleLocked();
2131 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
2135 mAngleConstraint->toggleRelative();
2142 else if ( type == QEvent::KeyPress )
2144 mAngleLineEdit->setFocus();
2145 mAngleLineEdit->selectAll();
2154 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
2163 else if ( type == QEvent::KeyPress )
2165 mDistanceLineEdit->setFocus();
2166 mDistanceLineEdit->selectAll();
2174 if ( type == QEvent::KeyPress )
2176 setConstructionMode( !mConstructionMode );
2183 if ( type == QEvent::KeyPress )
2185 const bool parallel = mParallelAction->isChecked();
2186 const bool perpendicular = mPerpendicularAction->isChecked();
2188 if ( !parallel && !perpendicular )
2192 else if ( perpendicular )
2209 if ( type == QEvent::ShortcutOverride )
2211 const QList<double> constActionKeys { mCommonAngleActions.keys() };
2212 const int currentAngleActionIndex {
static_cast<int>( constActionKeys.indexOf( mCommonAngleConstraint ) ) };
2213 const QList<QAction *> constActions { mCommonAngleActions.values() };
2214 QAction *nextAngleAction;
2215 if ( e->modifiers() == Qt::ShiftModifier )
2217 nextAngleAction = currentAngleActionIndex == 0 ? constActions.last() : constActions.at( currentAngleActionIndex - 1 );
2221 nextAngleAction = currentAngleActionIndex == constActions.count() - 1 ? constActions.first() : constActions.at( currentAngleActionIndex + 1 );
2223 nextAngleAction->trigger();
2233 return e->isAccepted();
2240 if ( mMapCanvas->mapSettings().destinationCrs().isGeographic() )
2242 mAngleLineEdit->setToolTip( tr(
"Angle constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
2243 mDistanceLineEdit->setToolTip( tr(
"Distance constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
2245 mLabelX->setText( tr(
"Long" ) );
2246 mLabelY->setText( tr(
"Lat" ) );
2248 mXConstraint->setPrecision( 8 );
2249 mYConstraint->setPrecision( 8 );
2253 mAngleLineEdit->setToolTip(
"<b>" + tr(
"Angle" ) +
"</b><br>(" + tr(
"press a for quick access" ) +
")" );
2254 mAngleLineEdit->setToolTip( QString() );
2256 mDistanceLineEdit->setToolTip(
"<b>" + tr(
"Distance" ) +
"</b><br>(" + tr(
"press d for quick access" ) +
")" );
2258 mLabelX->setText( tr(
"x" ) );
2259 mLabelY->setText( tr(
"y" ) );
2261 mXConstraint->setPrecision( 6 );
2262 mYConstraint->setPrecision( 6 );
2267 mEnableAction->setEnabled(
true );
2268 mErrorLabel->hide();
2271 mCurrentMapToolSupportsCad =
true;
2273 if ( mSessionActive && !isVisible() )
2278 setCadEnabled( mSessionActive );
2280 if ( !mConstructionGuidesLayer )
2282 resetConstructionGuides();
2285 if ( mDeferredUpdateConstructionGuidesCrs )
2287 updateConstructionGuidesCrs();
2297 mEnableAction->setEnabled(
false );
2298 mErrorLabel->setText( tr(
"Advanced digitizing tools are not enabled for the current map tool" ) );
2299 mErrorLabel->show();
2302 mCurrentMapToolSupportsCad =
false;
2304 mSnapIndicator->setVisible(
false );
2306 setCadEnabled(
false );
2311 mCadPaintItem->update();
2316 if ( !force && ( mLineExtensionConstraint->isLocked() || mXyVertexConstraint->isLocked() ) )
2321 mLockedSnapVertices.clear();
2326 QgsPoint pt = pointXYToPoint( point );
2329 mCadPointList << pt;
2333 mCadPointList.insert( 0, pt );
2340 mConstructionGuideLine.addVertex( pt );
2342 if ( mConstructionGuideLine.numPoints() == 2 )
2345 QgsGeometry geom( mConstructionGuideLine.clone() );
2347 ( void ) mConstructionGuidesLayer->dataProvider()->addFeature( feature );
2348 mConstructionGuideId = feature.
id();
2350 else if ( mConstructionGuideLine.numPoints() > 2 )
2352 QgsGeometry geom( mConstructionGuideLine.clone() );
2353 ( void ) mConstructionGuidesLayer->dataProvider()->changeGeometryValues( { { mConstructionGuideId, geom } } );
2358 if ( !mConstructionGuideLine.isEmpty() )
2360 mConstructionGuideLine.addVertex( pt );
2362 QgsGeometry geom( mConstructionGuideLine.clone() );
2363 ( void ) mConstructionGuidesLayer->dataProvider()->changeGeometryValues( { { mConstructionGuideId, geom } } );
2364 mConstructionGuideLine.clear();
2379 mCadPointList.removeAt( i );
2386 mCadPointList.clear();
2387 mSnappedSegment.clear();
2397 mCadPointList << point;
2402 mCadPointList[0] = point;
2409 if ( mode == mLockMode )
2414 mLockerButton->setChecked( mode ==
HardLock );
2415 if ( mRepeatingLockButton )
2419 mRepeatingLockButton->setEnabled(
true );
2423 mRepeatingLockButton->setChecked(
false );
2424 mRepeatingLockButton->setEnabled(
false );
2436 mRepeatingLock = repeating;
2437 if ( mRepeatingLockButton )
2438 mRepeatingLockButton->setChecked( repeating );
2444 if ( mRelativeButton )
2446 mRelativeButton->setChecked(
relative );
2453 if ( updateWidget && mLineEdit->isEnabled() )
2459 switch ( mCadConstraintType )
2463 return QLocale().toString( mValue,
'f', mPrecision ).append( tr(
" °" ) );
2468 if ( mMapCanvas->mapSettings().destinationCrs().isGeographic() )
2470 return QLocale().toString( mValue,
'f', mPrecision ).append( tr(
" °" ) );
2474 return QLocale().toString( mValue,
'f', mPrecision );
2491 return QLocale().toString( mValue,
'f', mPrecision );
2507 if ( mLineEdit->isEnabled() )
2513 return mCadConstraintType;
2518 mCadConstraintType = constraintType;
2523 mMapCanvas = mapCanvas;
2528 QString
value { text.trimmed() };
2529 switch ( constraintType )
2535 if (
value.endsWith( distanceUnit ) )
2537 value.chop( distanceUnit.length() );
2544 const QString angleUnit { tr(
"°" ) };
2545 if (
value.endsWith( angleUnit ) )
2547 value.chop( angleUnit.length() );
2554 return value.trimmed();
2562 return mCadPointList.value( 0 );
2571 QgsPoint res = mCadPointList.value( 0 );
2572 const QgsPointXY layerCoordinates = mMapCanvas->mapSettings().mapToLayerCoordinates( layer, res );
2573 res.
setX( layerCoordinates.
x() );
2574 res.
setY( layerCoordinates.
y() );
2585 return mCadPointList.value( 1 );
2595 return mCadPointList.value( 2 );
2600QgsPoint QgsAdvancedDigitizingDockWidget::pointXYToPoint(
const QgsPointXY &point )
const
2607 return mZLineEdit->isEnabled() ? QLocale().toDouble( mZLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
2612 return mMLineEdit->isEnabled() ? QLocale().toDouble( mMLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
2617 mWeightValue = value;
2618 const bool wasEnabled = mWeightEnabled;
2619 mWeightEnabled = enabled;
2622 if ( wasEnabled != enabled )
2630 return mWeightValue;
2635 return mShowConstructionGuides ? mShowConstructionGuides->isChecked() :
false;
2640 return mSnapToConstructionGuides ? mShowConstructionGuides->isChecked() && mSnapToConstructionGuides->isChecked() :
false;
2645 return mRecordConstructionGuides ? mRecordConstructionGuides->isChecked() :
false;
2648void QgsAdvancedDigitizingDockWidget::updateConstructionGuidesCrs()
2650 if ( !mConstructionGuidesLayer )
2657 mDeferredUpdateConstructionGuidesCrs =
true;
2660 QgsCoordinateTransform transform = QgsCoordinateTransform( mConstructionGuidesLayer->crs(), mMapCanvas->mapSettings().destinationCrs(),
QgsProject::instance()->
transformContext() );
2661 mConstructionGuidesLayer->setCrs( mMapCanvas->mapSettings().destinationCrs() );
2662 QgsFeatureIterator it = mConstructionGuidesLayer->getFeatures( QgsFeatureRequest().setNoAttributes() );
2666 QgsGeometry geom = feature.
geometry();
2668 mConstructionGuidesLayer->dataProvider()->changeGeometryValues( { { feature.
id(), geom } } );
2671 mDeferredUpdateConstructionGuidesCrs =
false;
2674void QgsAdvancedDigitizingDockWidget::resetConstructionGuides()
2676 if ( mConstructionGuidesLayer )
2678 mConstructionGuidesLayer.reset();
2681 const QgsVectorLayer::LayerOptions options(
QgsProject::instance()->transformContext(),
false,
false );
2682 mConstructionGuidesLayer = std::make_unique<QgsVectorLayer>( u
"LineString?crs=%1"_s.arg( mMapCanvas->mapSettings().destinationCrs().authid() ), u
"constructionGuides"_s, u
"memory"_s, options );
DistanceUnit
Units of distance.
CadConstraintType
Advanced digitizing constraint type.
@ Distance
Distance value.
@ YCoordinate
Y Coordinate value.
@ XCoordinate
X Coordinate value.
CadMeasurementDisplayType
Advanced digitizing measurement display types.
@ Hidden
Hide measurement.
@ Cartesian
Use Cartesian measurements.
@ Ellipsoidal
Use Ellipsoidal measurements.
@ 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...
@ Distance
Distance (segment length).
@ TotalLength
Total length (or perimeter).
@ MCoordinate
M coordinate.
@ Angle
Angle between segments.
@ Bearing
Segment bearing.
@ Weight
Weight for NURBSCurve.
@ ZCoordinate
Z coordinate.
@ YCoordinate
Y coordinate.
@ XCoordinate
X coordinate.
@ CommonAngleSnapping
Common angles.
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.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
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 QgsGeometry with associated coordinate reference system.
A boolean settings entry.
static QgsSettingsTreeNode * sTreeDigitizing
static QgsSettingsTreeNode * sTreeCad
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.
T enumValue(const QString &key, const T &defaultValue, const Section section=NoSection)
Returns the setting value for a setting based on an enum.
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.