44#include <QActionGroup>
45#include <QCoreApplication>
50#include "moc_qgsadvanceddigitizingdockwidget.cpp"
52using namespace Qt::StringLiterals;
54const QgsSettingsEntryBool *QgsAdvancedDigitizingDockWidget::settingsCadSnappingPriorityPrioritizeFeature
66 , mMapCanvas( canvas )
67 , mUserInputWidget( userInputWidget )
69 , mCommonAngleConstraint(
QgsSettings().value( u
"/Cad/CommonAngle"_s, 0.0 ).toDouble() )
75 mAngleConstraint = std::make_unique<CadConstraint>( mAngleLineEdit, mLockAngleButton, mRelativeAngleButton, mRepeatingLockAngleButton );
77 mAngleConstraint->setMapCanvas( mMapCanvas );
78 mDistanceConstraint = std::make_unique<CadConstraint>( mDistanceLineEdit, mLockDistanceButton,
nullptr, mRepeatingLockDistanceButton );
80 mDistanceConstraint->setMapCanvas( mMapCanvas );
81 mXConstraint = std::make_unique<CadConstraint>( mXLineEdit, mLockXButton, mRelativeXButton, mRepeatingLockXButton );
83 mXConstraint->setMapCanvas( mMapCanvas );
84 mYConstraint = std::make_unique<CadConstraint>( mYLineEdit, mLockYButton, mRelativeYButton, mRepeatingLockYButton );
86 mYConstraint->setMapCanvas( mMapCanvas );
87 mZConstraint = std::make_unique<CadConstraint>( mZLineEdit, mLockZButton, mRelativeZButton, mRepeatingLockZButton );
89 mZConstraint->setMapCanvas( mMapCanvas );
90 mMConstraint = std::make_unique<CadConstraint>( mMLineEdit, mLockMButton, mRelativeMButton, mRepeatingLockMButton );
92 mMConstraint->setMapCanvas( mMapCanvas );
94 mLineExtensionConstraint = std::make_unique<CadConstraint>(
new QLineEdit(),
new QToolButton() );
95 mXyVertexConstraint = std::make_unique<CadConstraint>(
new QLineEdit(),
new QToolButton() );
96 mXyVertexConstraint->setMapCanvas( mMapCanvas );
100 mMapCanvas->installEventFilter(
this );
101 mAngleLineEdit->installEventFilter(
this );
102 mDistanceLineEdit->installEventFilter(
this );
103 mXLineEdit->installEventFilter(
this );
104 mYLineEdit->installEventFilter(
this );
105 mZLineEdit->installEventFilter(
this );
106 mMLineEdit->installEventFilter(
this );
109 connect( mEnableAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::activateCad );
110 connect( mConstructionModeAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::setConstructionMode );
111 connect( mParallelAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
112 connect( mPerpendicularAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
113 connect( mLockAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
114 connect( mLockDistanceButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
115 connect( mLockXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
116 connect( mLockYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
117 connect( mLockZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
118 connect( mLockMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
119 connect( mRelativeAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
120 connect( mRelativeXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
121 connect( mRelativeYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
122 connect( mRelativeZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
123 connect( mRelativeMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
124 connect( mRepeatingLockDistanceButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
125 connect( mRepeatingLockAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
126 connect( mRepeatingLockXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
127 connect( mRepeatingLockYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
128 connect( mRepeatingLockZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
129 connect( mRepeatingLockMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
130 connect( mAngleLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
131 connect( mDistanceLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
132 connect( mXLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
133 connect( mYLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
134 connect( mZLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
135 connect( mMLineEdit, &QLineEdit::returnPressed,
this, [
this]() { lockConstraint(); } );
136 connect( mAngleLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
137 connect( mDistanceLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
138 connect( mXLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
139 connect( mYLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
140 connect( mZLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
141 connect( mMLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
147 whileBlocking( mAngleLineEdit )->setText( cleanedInputValue );
153 whileBlocking( mDistanceLineEdit )->setText( cleanedInputValue );
165 mCommonAngleActionsMenu =
new QMenu(
this );
167#ifndef __clang_analyzer__
168 QActionGroup *angleButtonGroup =
new QActionGroup( mCommonAngleActionsMenu );
170 QList<QPair<double, QString>> commonAngles;
171 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 } );
172 for ( QList<double>::const_iterator it = anglesDouble.constBegin(); it != anglesDouble.constEnd(); ++it )
178 QMenu *snappingPriorityMenu =
new QMenu( tr(
"Snapping Priority" ), mCommonAngleActionsMenu );
179 QActionGroup *snappingPriorityActionGroup =
new QActionGroup( snappingPriorityMenu );
180 QAction *featuresAction =
new QAction( tr(
"Prioritize Snapping to Features" ), snappingPriorityActionGroup );
181 featuresAction->setCheckable(
true );
182 QAction *anglesAction =
new QAction( tr(
"Prioritize Snapping to Common Angles" ), snappingPriorityActionGroup );
183 anglesAction->setCheckable(
true );
184 snappingPriorityActionGroup->addAction( featuresAction );
185 snappingPriorityActionGroup->addAction( anglesAction );
186 snappingPriorityMenu->addAction( anglesAction );
187 snappingPriorityMenu->addAction( featuresAction );
188 connect( anglesAction, &QAction::changed,
this, [
this, featuresAction] {
189 mSnappingPrioritizeFeatures = featuresAction->isChecked();
190 settingsCadSnappingPriorityPrioritizeFeature->setValue( featuresAction->isChecked() );
192 featuresAction->setChecked( settingsCadSnappingPriorityPrioritizeFeature->value() );
193 anglesAction->setChecked( !featuresAction->isChecked() );
194 mCommonAngleActionsMenu->addMenu( snappingPriorityMenu );
197 for ( QList<QPair<double, QString>>::const_iterator it = commonAngles.constBegin(); it != commonAngles.constEnd(); ++it )
199 QAction *action =
new QAction( it->second, mCommonAngleActionsMenu );
200 action->setCheckable(
true );
201 action->setChecked( it->first == mCommonAngleConstraint );
202 mCommonAngleActionsMenu->addAction( action );
204#ifndef __clang_analyzer__
205 angleButtonGroup->addAction( action );
207 mCommonAngleActions.insert( it->first, action );
211 QMenu *constructionSettingsMenu =
new QMenu(
this );
213 mRecordConstructionGuides =
new QAction( tr(
"Record Construction Guides" ), constructionSettingsMenu );
214 mRecordConstructionGuides->setCheckable(
true );
215 mRecordConstructionGuides->setChecked( settingsCadRecordConstructionGuides->value() );
216 constructionSettingsMenu->addAction( mRecordConstructionGuides );
217 connect( mRecordConstructionGuides, &QAction::triggered,
this, [
this]() { settingsCadRecordConstructionGuides->setValue( mRecordConstructionGuides->isChecked() ); } );
219 mShowConstructionGuides =
new QAction( tr(
"Show Construction Guides" ), constructionSettingsMenu );
220 mShowConstructionGuides->setCheckable(
true );
221 mShowConstructionGuides->setChecked( settingsCadShowConstructionGuides->value() );
222 constructionSettingsMenu->addAction( mShowConstructionGuides );
223 connect( mShowConstructionGuides, &QAction::triggered,
this, [
this]() {
224 settingsCadShowConstructionGuides->setValue( mShowConstructionGuides->isChecked() );
228 mSnapToConstructionGuides =
new QAction( tr(
"Snap to Visible Construction Guides" ), constructionSettingsMenu );
229 mSnapToConstructionGuides->setCheckable(
true );
230 mSnapToConstructionGuides->setChecked( settingsCadSnapToConstructionGuides->value() );
231 constructionSettingsMenu->addAction( mSnapToConstructionGuides );
232 connect( mSnapToConstructionGuides, &QAction::triggered,
this, [
this]() { settingsCadSnapToConstructionGuides->setValue( mSnapToConstructionGuides->isChecked() ); } );
234 constructionSettingsMenu->addSeparator();
236 mClearConstructionGuides =
new QAction( tr(
"Clear Construction Guides" ), constructionSettingsMenu );
237 constructionSettingsMenu->addAction( mClearConstructionGuides );
238 connect( mClearConstructionGuides, &QAction::triggered,
this, [
this]() {
239 resetConstructionGuides();
243 QToolButton *constructionModeToolButton = qobject_cast<QToolButton *>( mToolbar->widgetForAction( mConstructionModeAction ) );
244 constructionModeToolButton->setPopupMode( QToolButton::MenuButtonPopup );
245 constructionModeToolButton->setMenu( constructionSettingsMenu );
246 constructionModeToolButton->setObjectName( u
"ConstructionModeButton"_s );
249 QMenu *toolsMenu =
new QMenu(
this );
250 connect( toolsMenu, &QMenu::aboutToShow,
this, [
this, toolsMenu]() {
253 for (
const QString &name : toolMetadataNames )
256 QAction *toolAction =
new QAction( toolMetadata->
icon(), toolMetadata->
visibleName(), toolsMenu );
257 connect( toolAction, &QAction::triggered,
this, [
this, toolMetadata]() {
setTool( toolMetadata->
createTool( mMapCanvas,
this ) ); } );
258 toolsMenu->addAction( toolAction );
261 qobject_cast<QToolButton *>( mToolbar->widgetForAction( mToolsAction ) )->setPopupMode( QToolButton::InstantPopup );
262 mToolsAction->setMenu( toolsMenu );
264 qobject_cast<QToolButton *>( mToolbar->widgetForAction( mSettingsAction ) )->setPopupMode( QToolButton::InstantPopup );
265 mSettingsAction->setMenu( mCommonAngleActionsMenu );
266 mSettingsAction->setCheckable(
true );
267 mSettingsAction->setToolTip(
"<b>" + tr(
"Snap to common angles" ) +
"</b><br>(" + tr(
"press n to cycle through the options" ) +
")" );
268 mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
269 connect( mCommonAngleActionsMenu, &QMenu::triggered,
this, &QgsAdvancedDigitizingDockWidget::settingsButtonTriggered );
272 QMenu *constructionMenu =
new QMenu(
this );
274 mLineExtensionAction =
new QAction( tr(
"Line Extension" ), constructionMenu );
275 mLineExtensionAction->setCheckable(
true );
276 constructionMenu->addAction( mLineExtensionAction );
277 connect( mLineExtensionAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
279 mXyVertexAction =
new QAction( tr(
"X/Y Point" ), constructionMenu );
280 mXyVertexAction->setCheckable(
true );
281 constructionMenu->addAction( mXyVertexAction );
282 connect( mXyVertexAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
284 auto constructionToolBar = qobject_cast<QToolButton *>( mToolbar->widgetForAction( mConstructionAction ) );
285 constructionToolBar->setPopupMode( QToolButton::InstantPopup );
286 mConstructionAction->setMenu( constructionMenu );
287 constructionToolBar->setObjectName( u
"ConstructionButton"_s );
288 mConstructionAction->setCheckable(
true );
289 mConstructionAction->setToolTip( tr(
"Construction Tools" ) );
292 mConstructionModeAction->setToolTip(
"<b>" + tr(
"Construction mode" ) +
"</b><br>(" + tr(
"press c to toggle on/off" ) +
")" );
293 mDistanceLineEdit->setToolTip(
"<b>" + tr(
"Distance" ) +
"</b><br>(" + tr(
"press d for quick access" ) +
")" );
294 mLockDistanceButton->setToolTip(
"<b>" + tr(
"Lock distance" ) +
"</b><br>(" + tr(
"press Ctrl + d for quick access" ) +
")" );
295 mRepeatingLockDistanceButton->setToolTip(
"<b>" + tr(
"Continuously lock distance" ) +
"</b>" );
297 mRelativeAngleButton->setToolTip(
"<b>" + tr(
"Toggles relative angle to previous segment" ) +
"</b><br>(" + tr(
"press Shift + a for quick access" ) +
")" );
298 mAngleLineEdit->setToolTip(
"<b>" + tr(
"Angle" ) +
"</b><br>(" + tr(
"press a for quick access" ) +
")" );
299 mLockAngleButton->setToolTip(
"<b>" + tr(
"Lock angle" ) +
"</b><br>(" + tr(
"press Ctrl + a for quick access" ) +
")" );
300 mRepeatingLockAngleButton->setToolTip(
"<b>" + tr(
"Continuously lock angle" ) +
"</b>" );
302 mRelativeXButton->setToolTip(
"<b>" + tr(
"Toggles relative x to previous node" ) +
"</b><br>(" + tr(
"press Shift + x for quick access" ) +
")" );
303 mXLineEdit->setToolTip(
"<b>" + tr(
"X coordinate" ) +
"</b><br>(" + tr(
"press x for quick access" ) +
")" );
304 mLockXButton->setToolTip(
"<b>" + tr(
"Lock x coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + x for quick access" ) +
")" );
305 mRepeatingLockXButton->setToolTip(
"<b>" + tr(
"Continuously lock x coordinate" ) +
"</b>" );
307 mRelativeYButton->setToolTip(
"<b>" + tr(
"Toggles relative y to previous node" ) +
"</b><br>(" + tr(
"press Shift + y for quick access" ) +
")" );
308 mYLineEdit->setToolTip(
"<b>" + tr(
"Y coordinate" ) +
"</b><br>(" + tr(
"press y for quick access" ) +
")" );
309 mLockYButton->setToolTip(
"<b>" + tr(
"Lock y coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + y for quick access" ) +
")" );
310 mRepeatingLockYButton->setToolTip(
"<b>" + tr(
"Continuously lock y coordinate" ) +
"</b>" );
312 mRelativeZButton->setToolTip(
"<b>" + tr(
"Toggles relative z to previous node" ) +
"</b><br>(" + tr(
"press Shift + z for quick access" ) +
")" );
313 mZLineEdit->setToolTip(
"<b>" + tr(
"Z coordinate" ) +
"</b><br>(" + tr(
"press z for quick access" ) +
")" );
314 mLockZButton->setToolTip(
"<b>" + tr(
"Lock z coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + z for quick access" ) +
")" );
315 mRepeatingLockZButton->setToolTip(
"<b>" + tr(
"Continuously lock z coordinate" ) +
"</b>" );
317 mRelativeMButton->setToolTip(
"<b>" + tr(
"Toggles relative m to previous node" ) +
"</b><br>(" + tr(
"press Shift + m for quick access" ) +
")" );
318 mMLineEdit->setToolTip(
"<b>" + tr(
"M coordinate" ) +
"</b><br>(" + tr(
"press m for quick access" ) +
")" );
319 mLockMButton->setToolTip(
"<b>" + tr(
"Lock m coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + m for quick access" ) +
")" );
320 mRepeatingLockMButton->setToolTip(
"<b>" + tr(
"Continuously lock m coordinate" ) +
"</b>" );
331 mFloaterActionsMenu =
new QMenu(
this );
332 qobject_cast<QToolButton *>( mToolbar->widgetForAction( mFloaterAction ) )->setPopupMode( QToolButton::InstantPopup );
333 mFloaterAction->setMenu( mFloaterActionsMenu );
334 mFloaterAction->setCheckable(
true );
336 mFloaterAction->setChecked( mFloater->active() );
340 QAction *action =
new QAction( tr(
"Show Floater" ), mFloaterActionsMenu );
341 action->setCheckable(
true );
342 action->setChecked( mFloater->active() );
343 mFloaterActionsMenu->addAction( action );
344 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
345 mFloater->setActive( checked );
346 mFloaterAction->setChecked( checked );
350 mFloaterActionsMenu->addSeparator();
353 QAction *action =
new QAction( tr(
"Show Distance" ), mFloaterActionsMenu );
354 action->setCheckable(
true );
355 mFloaterActionsMenu->addAction( action );
357 const bool isDistanceChecked =
QgsSettings().
value( u
"/Cad/DistanceShowInFloater"_s,
true ).toBool();
358 action->setChecked( isDistanceChecked );
364 QAction *action =
new QAction( tr(
"Show Angle" ), mFloaterActionsMenu );
365 action->setCheckable(
true );
366 mFloaterActionsMenu->addAction( action );
368 const bool isAngleChecked =
QgsSettings().
value( u
"/Cad/AngleShowInFloater"_s,
true ).toBool();
369 action->setChecked( isAngleChecked );
375 QAction *action =
new QAction( tr(
"Show XY Coordinates" ), mFloaterActionsMenu );
376 action->setCheckable(
true );
377 mFloaterActionsMenu->addAction( action );
378 connect( action, &QAction::toggled,
this, [
this](
bool checked ) {
383 const bool isXCoordinateChecked =
QgsSettings().
value( u
"/Cad/XCoordinateShowInFloater"_s,
true ).toBool();
384 action->setChecked( isXCoordinateChecked );
392 QAction *action =
new QAction( tr(
"Show Z Value" ), mFloaterActionsMenu );
393 action->setCheckable(
true );
394 mFloaterActionsMenu->addAction( action );
396 const bool isZCoordinateChecked =
QgsSettings().
value( u
"/Cad/ZCoordinateShowInFloater"_s,
true ).toBool();
397 action->setChecked( isZCoordinateChecked );
404 QAction *action =
new QAction( tr(
"Show M Value" ), mFloaterActionsMenu );
405 action->setCheckable(
true );
406 mFloaterActionsMenu->addAction( action );
408 const bool isMCoordinateChecked =
QgsSettings().
value( u
"/Cad/MCoordinateShowInFloater"_s,
true ).toBool();
409 action->setChecked( isMCoordinateChecked );
416 QAction *action =
new QAction( tr(
"Show Bearing/Azimuth" ), mFloaterActionsMenu );
417 action->setCheckable(
true );
418 mFloaterActionsMenu->addAction( action );
420 const bool isBearingChecked =
QgsSettings().
value( u
"/Cad/BearingShowInFloater"_s,
false ).toBool();
421 action->setChecked( isBearingChecked );
428 QAction *action =
new QAction( tr(
"Show Common Snapping Angle" ), mFloaterActionsMenu );
429 action->setCheckable(
true );
430 mFloaterActionsMenu->addAction( action );
432 const bool isCommonAngleSnappingChecked =
QgsSettings().
value( u
"/Cad/CommonAngleSnappingShowInFloater"_s,
false ).toBool();
433 action->setChecked( isCommonAngleSnappingChecked );
440 QMenu *menu =
new QMenu( tr(
"Show Area" ), mFloaterActionsMenu );
441 mFloaterActionsMenu->addMenu( menu );
443 QAction *actionHidden =
new QAction( tr(
"Hidden" ), mFloaterActionsMenu );
445 actionHidden->setCheckable(
true );
446 menu->addAction( actionHidden );
448 QAction *actionEllipsoidal =
new QAction( tr(
"Show Ellipsoidal Area" ), mFloaterActionsMenu );
450 actionEllipsoidal->setCheckable(
true );
451 menu->addAction( actionEllipsoidal );
453 QAction *actionCartesian =
new QAction( tr(
"Show Cartesian Area" ), mFloaterActionsMenu );
455 actionCartesian->setCheckable(
true );
456 menu->addAction( actionCartesian );
458 QActionGroup *group =
new QActionGroup( menu );
459 group->addAction( actionHidden );
460 group->addAction( actionEllipsoidal );
461 group->addAction( actionCartesian );
464 for ( QAction *action : group->actions() )
468 action->setChecked(
true );
470 connect( action, &QAction::toggled,
this, [
this, action](
bool checked ) {
481 QMenu *menu =
new QMenu( tr(
"Show Total Length/Perimeter" ), mFloaterActionsMenu );
482 mFloaterActionsMenu->addMenu( menu );
484 QAction *actionHidden =
new QAction( tr(
"Hidden" ), mFloaterActionsMenu );
486 actionHidden->setCheckable(
true );
487 menu->addAction( actionHidden );
489 QAction *actionEllipsoidal =
new QAction( tr(
"Show Ellipsoidal Lengths" ), mFloaterActionsMenu );
491 actionEllipsoidal->setCheckable(
true );
492 menu->addAction( actionEllipsoidal );
494 QAction *actionCartesian =
new QAction( tr(
"Show Cartesian Lengths" ), mFloaterActionsMenu );
496 actionCartesian->setCheckable(
true );
497 menu->addAction( actionCartesian );
499 QActionGroup *group =
new QActionGroup( menu );
500 group->addAction( actionHidden );
501 group->addAction( actionEllipsoidal );
502 group->addAction( actionCartesian );
505 for ( QAction *action : group->actions() )
509 action->setChecked(
true );
511 connect( action, &QAction::toggled,
this, [
this, action](
bool checked ) {
522 QAction *action =
new QAction( tr(
"Show Weight" ), mFloaterActionsMenu );
523 action->setCheckable(
true );
524 mFloaterActionsMenu->addAction( action );
526 const bool isWeightChecked =
QgsSettings().
value( u
"/Cad/WeightShowInFloater"_s,
true ).toBool();
527 action->setChecked( isWeightChecked );
532 updateCapacity(
true );
545 mCurrentTool->deleteLater();
552 return tr(
"Do Not Snap to Common Angles" );
554 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 );
573 QString totalLengthString;
576 mMapCanvas->mapSettings().destinationCrs(),
590 mXLineEdit->setText( value );
593 emit mXLineEdit->returnPressed();
597 QEvent *e =
new QEvent( QEvent::FocusOut );
598 QCoreApplication::postEvent( mXLineEdit, e );
602 emit mXLineEdit->textEdited( value );
607 mYLineEdit->setText( value );
610 emit mYLineEdit->returnPressed();
614 QEvent *e =
new QEvent( QEvent::FocusOut );
615 QCoreApplication::postEvent( mYLineEdit, e );
619 emit mYLineEdit->textEdited( value );
624 mZLineEdit->setText( value );
627 emit mZLineEdit->returnPressed();
631 QEvent *e =
new QEvent( QEvent::FocusOut );
632 QCoreApplication::postEvent( mZLineEdit, e );
636 emit mZLineEdit->textEdited( value );
641 mMLineEdit->setText( value );
644 emit mMLineEdit->returnPressed();
648 QEvent *e =
new QEvent( QEvent::FocusOut );
649 QCoreApplication::postEvent( mMLineEdit, e );
653 emit mMLineEdit->textEdited( value );
658 mAngleLineEdit->setText( value );
661 emit mAngleLineEdit->returnPressed();
665 emit mAngleLineEdit->textEdited( value );
670 mDistanceLineEdit->setText( value );
673 emit mDistanceLineEdit->returnPressed();
677 QEvent *e =
new QEvent( QEvent::FocusOut );
678 QCoreApplication::postEvent( mDistanceLineEdit, e );
682 emit mDistanceLineEdit->textEdited( value );
687void QgsAdvancedDigitizingDockWidget::setCadEnabled(
bool enabled )
689 mCadEnabled = enabled;
690 mEnableAction->setChecked( enabled );
691 mConstructionModeAction->setEnabled( enabled );
692 mSettingsAction->setEnabled( enabled );
693 mInputWidgets->setEnabled( enabled );
694 mFloaterAction->setEnabled( enabled );
695 mConstructionAction->setEnabled( enabled );
696 mToolsAction->setEnabled( enabled );
701 mLineExtensionAction->setChecked(
false );
702 mXyVertexAction->setChecked(
false );
704 mParallelAction->setEnabled(
false );
705 mPerpendicularAction->setEnabled(
false );
708 mCurrentTool->deleteLater();
715 setConstructionMode(
false );
725 mLastSnapMatch = QgsPointLocator::Match();
731 bool enableZ =
false;
732 bool enableM =
false;
736 switch ( layer->type() )
749 QgsMeshLayer *mlayer = qobject_cast<QgsMeshLayer *>( layer );
765 mTargetLayerSupportsM = enableM;
766 mTargetLayerSupportsZ = enableZ;
774 if (
enable && !mTargetLayerSupportsZ )
778 mRelativeZButton->setEnabled(
enable );
779 mZLabel->setEnabled(
enable );
780 mZLineEdit->setEnabled(
enable );
781 if ( mZLineEdit->isEnabled() )
785 mLockZButton->setEnabled(
enable );
791 if (
enable && !mTargetLayerSupportsM )
795 mRelativeMButton->setEnabled(
enable );
796 mMLabel->setEnabled(
enable );
797 mMLineEdit->setEnabled(
enable );
798 if ( mMLineEdit->isEnabled() )
802 mLockMButton->setEnabled(
enable );
806void QgsAdvancedDigitizingDockWidget::activateCad(
bool enabled )
808 enabled &= mCurrentMapToolSupportsCad;
810 mSessionActive = enabled;
812 if ( enabled && !isVisible() )
817 setCadEnabled( enabled );
824 mCurrentTool->deleteLater();
825 mCurrentTool =
nullptr;
832 if ( QWidget *toolWidget = mCurrentTool->createWidget() )
834 toolWidget->setParent( mUserInputWidget );
835 mUserInputWidget->addUserInputWidget( toolWidget );
843 return mCurrentTool.data();
846void QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked(
bool activated )
852 else if ( sender() == mParallelAction )
856 else if ( sender() == mPerpendicularAction )
862void QgsAdvancedDigitizingDockWidget::setConstraintRelative(
bool activate )
864 if ( sender() == mRelativeAngleButton )
866 mAngleConstraint->setRelative( activate );
869 else if ( sender() == mRelativeXButton )
871 mXConstraint->setRelative( activate );
874 else if ( sender() == mRelativeYButton )
876 mYConstraint->setRelative( activate );
879 else if ( sender() == mRelativeZButton )
881 mZConstraint->setRelative( activate );
884 else if ( sender() == mRelativeMButton )
886 mMConstraint->setRelative( activate );
891void QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock(
bool activate )
893 if ( sender() == mRepeatingLockDistanceButton )
895 mDistanceConstraint->setRepeatingLock( activate );
897 else if ( sender() == mRepeatingLockAngleButton )
899 mAngleConstraint->setRepeatingLock( activate );
901 else if ( sender() == mRepeatingLockXButton )
903 mXConstraint->setRepeatingLock( activate );
905 else if ( sender() == mRepeatingLockYButton )
907 mYConstraint->setRepeatingLock( activate );
909 else if ( sender() == mRepeatingLockZButton )
911 mZConstraint->setRepeatingLock( activate );
913 else if ( sender() == mRepeatingLockMButton )
915 mMConstraint->setRepeatingLock( activate );
919void QgsAdvancedDigitizingDockWidget::setConstructionMode(
bool enabled )
921 mConstructionMode = enabled;
922 mConstructionModeAction->setChecked( enabled );
926 if ( enabled && mCadPointList.size() > 1 )
928 mConstructionGuideLine.addVertex( mCadPointList.at( 1 ) );
933void QgsAdvancedDigitizingDockWidget::settingsButtonTriggered( QAction *action )
936 for (
auto it = mCommonAngleActions.cbegin(); it != mCommonAngleActions.cend(); ++it )
938 if ( it.value() == action )
940 it.value()->setChecked(
true );
941 mCommonAngleConstraint = it.key();
942 QgsSettings().setValue( u
"/Cad/CommonAngle"_s, it.key() );
943 mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
950QgsMapLayer *QgsAdvancedDigitizingDockWidget::targetLayer()
const
952 if ( QgsMapToolAdvancedDigitizing *advancedTool = qobject_cast<QgsMapToolAdvancedDigitizing *>( mMapCanvas->mapTool() ) )
954 return advancedTool->layer();
958 return mMapCanvas->currentLayer();
968 if ( releaseRepeatingLocks )
970 mXyVertexAction->setChecked(
false );
974 mLineExtensionAction->setChecked(
false );
981 if ( releaseRepeatingLocks || !mAngleConstraint->isRepeatingLock() )
986 if ( releaseRepeatingLocks || !mDistanceConstraint->isRepeatingLock() )
991 if ( releaseRepeatingLocks || !mXConstraint->isRepeatingLock() )
996 if ( releaseRepeatingLocks || !mYConstraint->isRepeatingLock() )
1001 if ( releaseRepeatingLocks || !mZConstraint->isRepeatingLock() )
1006 if ( releaseRepeatingLocks || !mMConstraint->isRepeatingLock() )
1012 if ( !mCadPointList.empty() )
1014 if ( !mXConstraint->isLocked() && !mXConstraint->relative() )
1016 mXConstraint->setValue( mCadPointList.constLast().x(),
true );
1018 if ( !mYConstraint->isLocked() && !mYConstraint->relative() )
1020 mYConstraint->setValue( mCadPointList.constLast().y(),
true );
1022 if ( !mZConstraint->isLocked() && !mZConstraint->relative() )
1024 mZConstraint->setValue( mCadPointList.constLast().z(),
true );
1026 if ( !mMConstraint->isLocked() && !mMConstraint->relative() )
1028 mMConstraint->setValue( mCadPointList.constLast().m(),
true );
1034void QgsAdvancedDigitizingDockWidget::emit pointChanged()
1037 QPoint globalPos = mMapCanvas->cursor().pos();
1038 QPoint pos = mMapCanvas->mapFromGlobal( globalPos );
1039 QMouseEvent *e =
new QMouseEvent( QEvent::MouseMove, pos, globalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier );
1040 mCurrentMapTool->canvasMoveEvent( e );
1047 if ( obj == mAngleLineEdit || obj == mLockAngleButton )
1049 constraint = mAngleConstraint.get();
1051 else if ( obj == mDistanceLineEdit || obj == mLockDistanceButton )
1053 constraint = mDistanceConstraint.get();
1055 else if ( obj == mXLineEdit || obj == mLockXButton )
1057 constraint = mXConstraint.get();
1059 else if ( obj == mYLineEdit || obj == mLockYButton )
1061 constraint = mYConstraint.get();
1063 else if ( obj == mZLineEdit || obj == mLockZButton )
1065 constraint = mZConstraint.get();
1067 else if ( obj == mMLineEdit || obj == mLockMButton )
1069 constraint = mMConstraint.get();
1071 else if ( obj == mLineExtensionAction )
1073 constraint = mLineExtensionConstraint.get();
1075 else if ( obj == mXyVertexAction )
1077 constraint = mXyVertexConstraint.get();
1082double QgsAdvancedDigitizingDockWidget::parseUserInput(
const QString &inputValue,
const Qgis::CadConstraintType type,
bool &ok )
const
1092 QgsExpression expr( inputValue );
1093 const QVariant result = expr.evaluate();
1094 if ( expr.hasEvalError() )
1097 QString inputValueC { inputValue };
1100 if ( inputValue.contains( QLocale().groupSeparator() ) )
1102 inputValueC.remove( QLocale().groupSeparator() );
1103 QgsExpression exprC( inputValueC );
1104 const QVariant resultC = exprC.evaluate();
1105 if ( !exprC.hasEvalError() )
1107 value = resultC.toDouble( &ok );
1112 if ( !ok && QLocale().decimalPoint() != QChar(
'.' ) && inputValueC.contains( QLocale().decimalPoint() ) )
1114 QgsExpression exprC( inputValueC.replace( QLocale().decimalPoint(), QChar(
'.' ) ) );
1115 const QVariant resultC = exprC.evaluate();
1116 if ( !exprC.hasEvalError() )
1118 value = resultC.toDouble( &ok );
1124 value = result.toDouble( &ok );
1139void QgsAdvancedDigitizingDockWidget::updateConstraintValue( CadConstraint *constraint,
const QString &textValue,
bool convertExpression )
1141 if ( !constraint || textValue.isEmpty() )
1150 const double value = parseUserInput( textValue, constraint->cadConstraintType(), ok );
1154 constraint->setValue( value, convertExpression );
1159void QgsAdvancedDigitizingDockWidget::lockConstraint(
bool activate )
1169 const QString textValue = constraint->lineEdit()->text();
1170 if ( !textValue.isEmpty() )
1173 const double value = parseUserInput( textValue, constraint->cadConstraintType(), ok );
1176 constraint->setValue( value );
1190 if ( constraint == mXConstraint.get() )
1194 else if ( constraint == mYConstraint.get() )
1198 else if ( constraint == mZConstraint.get() )
1202 else if ( constraint == mMConstraint.get() )
1206 else if ( constraint == mDistanceConstraint.get() )
1210 else if ( constraint == mAngleConstraint.get() )
1218 if ( constraint == mAngleConstraint.get() )
1228void QgsAdvancedDigitizingDockWidget::constraintTextEdited(
const QString &textValue )
1236 updateConstraintValue( constraint, textValue,
false );
1239void QgsAdvancedDigitizingDockWidget::constraintFocusOut()
1241 QLineEdit *lineEdit = qobject_cast<QLineEdit *>( sender()->parent() );
1251 updateConstraintValue( constraint, lineEdit->text(),
true );
1256 mBetweenLineConstraint = constraint;
1261void QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint(
bool activate )
1271 if ( constraint == mXyVertexConstraint.get() )
1275 else if ( constraint == mLineExtensionConstraint.get() )
1289void QgsAdvancedDigitizingDockWidget::updateCapacity(
bool updateUIwithoutChange )
1292 const bool isGeographic = mMapCanvas->mapSettings().destinationCrs().isGeographic();
1295 if ( mCadPointList.count() > 1 )
1298 if ( !isGeographic )
1304 if ( mCadPointList.count() > 2 )
1306 if ( !isGeographic )
1309 if ( !updateUIwithoutChange && newCapacities == mCapacities )
1319 const bool distance = mCadEnabled && newCapacities.testFlag(
Distance );
1320 const bool relativeAngle = mCadEnabled && newCapacities.testFlag(
RelativeAngle );
1321 const bool absoluteAngle = mCadEnabled && newCapacities.testFlag(
AbsoluteAngle );
1322 const bool relativeCoordinates = mCadEnabled && newCapacities.testFlag(
RelativeCoordinates );
1324 mPerpendicularAction->setEnabled( distance && snappingEnabled );
1325 mParallelAction->setEnabled( distance && snappingEnabled );
1327 mLineExtensionAction->setEnabled( snappingEnabled );
1328 mXyVertexAction->setEnabled( snappingEnabled );
1332 if ( !snappingEnabled )
1334 mPerpendicularAction->setToolTip( tr(
"Snapping must be enabled to utilize perpendicular mode." ) );
1335 mParallelAction->setToolTip( tr(
"Snapping must be enabled to utilize parallel mode." ) );
1336 mLineExtensionAction->setToolTip( tr(
"Snapping must be enabled to utilize line extension mode." ) );
1337 mXyVertexAction->setToolTip( tr(
"Snapping must be enabled to utilize xy point mode." ) );
1339 else if ( mCadPointList.count() <= 1 )
1341 mPerpendicularAction->setToolTip( tr(
"A first vertex should be drawn to utilize perpendicular mode." ) );
1342 mParallelAction->setToolTip( tr(
"A first vertex should be drawn to utilize parallel mode." ) );
1344 else if ( isGeographic )
1346 mPerpendicularAction->setToolTip( tr(
"Perpendicular mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1347 mParallelAction->setToolTip( tr(
"Parallel mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1351 mPerpendicularAction->setToolTip(
"<b>" + tr(
"Perpendicular" ) +
"</b><br>(" + tr(
"press p to switch between perpendicular, parallel and normal mode" ) +
")" );
1352 mParallelAction->setToolTip(
"<b>" + tr(
"Parallel" ) +
"</b><br>(" + tr(
"press p to switch between perpendicular, parallel and normal mode" ) +
")" );
1356 if ( !absoluteAngle )
1362 mLockAngleButton->setEnabled( absoluteAngle );
1363 mRelativeAngleButton->setEnabled( relativeAngle );
1364 mAngleLineEdit->setEnabled( absoluteAngle );
1366 if ( !absoluteAngle )
1370 if ( !relativeAngle )
1372 mAngleConstraint->setRelative(
false );
1375 else if ( relativeAngle && !mCapacities.testFlag(
RelativeAngle ) )
1378 mAngleConstraint->setRelative(
true );
1383 mLockDistanceButton->setEnabled( distance && relativeCoordinates );
1384 mDistanceLineEdit->setEnabled( distance && relativeCoordinates );
1386 if ( !( distance && relativeCoordinates ) )
1391 mRelativeXButton->setEnabled( relativeCoordinates );
1392 mRelativeYButton->setEnabled( relativeCoordinates );
1393 mRelativeZButton->setEnabled( relativeCoordinates );
1394 mRelativeMButton->setEnabled( relativeCoordinates );
1397 mCapacities = newCapacities;
1398 mCadPaintItem->updatePosition();
1405 constr.
locked =
c->isLocked();
1407 constr.
value =
c->value();
1414 if ( !mLineExtensionConstraint->isLocked() && !mXyVertexConstraint->isLocked() )
1420 const int lastIndex = mLockedSnapVertices.length() - 1;
1421 for (
int i = lastIndex; i >= 0; --i )
1423 if ( mLockedSnapVertices[i].point() == snapMatch.
point() )
1425 if ( snapMatch.
point() != previouslySnap.
point() )
1427 mLockedSnapVertices.removeAt( i );
1433 if ( snapMatch.
point() != previouslySnap.
point() )
1435 mLockedSnapVertices.enqueue( snapMatch );
1438 if ( mLockedSnapVertices.count() > 3 )
1440 mLockedSnapVertices.dequeue();
1449 context.
xConstraint = _constraint( mXConstraint.get() );
1450 context.
yConstraint = _constraint( mYConstraint.get() );
1451 context.
zConstraint = _constraint( mZConstraint.get() );
1452 context.
mConstraint = _constraint( mMConstraint.get() );
1479 const bool res = output.
valid;
1481 mSnappedSegment.clear();
1486 mSnappedSegment << edgePt0 << edgePt1;
1507 mSnapIndicator->setMatch( output.
snapMatch );
1508 mSnapIndicator->setVisible(
true );
1512 mSnapIndicator->setVisible(
false );
1534 if ( mSnapMatch.layer() )
1538 if ( ( ( mSnapMatch.hasVertex() || mSnapMatch.hasLineEndpoint() ) && (
QgsPointXY( point ) == mSnapMatch.point() ) ) || ( mSnapMatch.hasEdge() &&
QgsProject::instance()->topologicalEditing() ) )
1541 point = mSnapMatch.interpolatedPoint( mMapCanvas->mapSettings().destinationCrs() );
1547 if ( mSnapMatch.hasVertex() || mSnapMatch.hasLineEndpoint() )
1549 toggleLockedSnapVertex( mSnapMatch, mLastSnapMatch );
1550 mLastSnapMatch = mSnapMatch;
1560 if ( mLockZButton->isChecked() )
1562 point.
setZ( QLocale().toDouble( mZLineEdit->text() ) );
1564 if ( mLockMButton->isChecked() )
1566 point.
setM( QLocale().toDouble( mMLineEdit->text() ) );
1572 updateUnlockedConstraintValues( point );
1580 emit
pushWarning( tr(
"Some constraints are incompatible. Resulting point might be incorrect." ) );
1587void QgsAdvancedDigitizingDockWidget::updateUnlockedConstraintValues(
const QgsPoint &point )
1589 bool previousPointExist, penulPointExist;
1594 if ( previousPointExist )
1596 double prevAngle = 0.0;
1598 if ( penulPointExist && mAngleConstraint->relative() )
1601 prevAngle = std::atan2( previousPt.
y() - penultimatePt.
y(), previousPt.
x() - penultimatePt.
x() ) * 180 / M_PI;
1604 const double xAngle { std::atan2( point.
y() - previousPt.
y(), point.
x() - previousPt.
x() ) * 180 / M_PI };
1607 const double angle = std::fmod( xAngle - prevAngle, 360.0 );
1608 if ( !mAngleConstraint->isLocked() )
1610 mAngleConstraint->setValue( angle );
1614 double bearing { std::fmod( xAngle, 360.0 ) };
1615 bearing = bearing <= 90.0 ? 90.0 - bearing : ( bearing > 90 ? 270.0 + 180.0 - bearing : 270.0 - bearing );
1616 const QgsNumericFormatContext context;
1621 if ( !mDistanceConstraint->isLocked() && previousPointExist )
1623 mDistanceConstraint->setValue( std::sqrt( previousPt.
distanceSquared( point ) ) );
1626 if ( !mXConstraint->isLocked() )
1628 if ( previousPointExist && mXConstraint->relative() )
1630 mXConstraint->setValue( point.
x() - previousPt.
x() );
1634 mXConstraint->setValue( point.
x() );
1638 if ( !mYConstraint->isLocked() )
1640 if ( previousPointExist && mYConstraint->relative() )
1642 mYConstraint->setValue( point.
y() - previousPt.
y() );
1646 mYConstraint->setValue( point.
y() );
1650 if ( !mZConstraint->isLocked() )
1652 if ( previousPointExist && mZConstraint->relative() )
1654 mZConstraint->setValue( point.
z() - previousPt.
z() );
1658 mZConstraint->setValue( point.
z() );
1662 if ( !mMConstraint->isLocked() )
1664 if ( previousPointExist && mMConstraint->relative() )
1666 mMConstraint->setValue( point.
m() - previousPt.
m() );
1670 mMConstraint->setValue( point.
m() );
1676QList<QgsPointXY> QgsAdvancedDigitizingDockWidget::snapSegmentToAllLayers(
const QgsPointXY &originalMapPoint,
bool *snapped )
const
1679 QgsPointXY pt1, pt2;
1680 QgsPointLocator::Match match;
1682 QgsSnappingUtils *snappingUtils = mMapCanvas->snappingUtils();
1684 const QgsSnappingConfig canvasConfig = snappingUtils->
config();
1685 QgsSnappingConfig localConfig = snappingUtils->
config();
1689 snappingUtils->
setConfig( localConfig );
1691 match = snappingUtils->
snapToMap( originalMapPoint,
nullptr,
true );
1693 snappingUtils->
setConfig( canvasConfig );
1703 *snapped =
segment.count() == 2;
1713 mCurrentTool->canvasPressEvent( event );
1718 event->setAccepted(
false );
1730 mCurrentTool->canvasMoveEvent( event );
1738 if ( event->button() == Qt::RightButton )
1742 mCurrentTool->canvasReleaseEvent( event );
1743 if ( !event->isAccepted() )
1755 event->setAccepted(
false );
1761 mCurrentTool->canvasReleaseEvent( event );
1762 if ( !event->isAccepted() )
1773 if ( mLockZButton->isChecked() )
1775 point.
setZ( QLocale().toDouble( mZLineEdit->text() ) );
1777 if ( mLockMButton->isChecked() )
1779 point.
setM( QLocale().toDouble( mMLineEdit->text() ) );
1790 event->setAccepted(
false );
1802 bool previousPointExist, penulPointExist, snappedSegmentExist;
1805 mSnappedSegment = snapSegmentToAllLayers( e->
originalMapPoint(), &snappedSegmentExist );
1807 if ( !previousPointExist || !snappedSegmentExist )
1812 double angle = std::atan2( mSnappedSegment[0].y() - mSnappedSegment[1].y(), mSnappedSegment[0].x() - mSnappedSegment[1].x() );
1814 if ( mAngleConstraint->relative() && penulPointExist )
1816 angle -= std::atan2( previousPt.
y() - penultimatePt.
y(), previousPt.
x() - penultimatePt.
x() );
1824 angle *= 180 / M_PI;
1826 mAngleConstraint->setValue( angle );
1827 mAngleConstraint->setLockMode( lockMode );
1845 case Qt::Key_Backspace:
1846 case Qt::Key_Delete:
1852 case Qt::Key_Escape:
1871 mCurrentTool->deleteLater();
1874 if ( !mConstructionGuideLine.isEmpty() )
1876 mConstructionGuideLine.clear();
1892 case Qt::Key_Backspace:
1893 case Qt::Key_Delete:
1899 case Qt::Key_Escape:
1903 if ( mConstructionGuideLine.numPoints() >= 2 )
1905 mConstructionGuidesLayer->dataProvider()->deleteFeatures(
QgsFeatureIds() << mConstructionGuideId );
1906 mConstructionGuideLine.clear();
1911 mCurrentTool->deleteLater();
1918 filterKeyPress( e );
1927 const auto constPoints = points;
1936 mDistanceConstraint->toggleLocked();
1941bool QgsAdvancedDigitizingDockWidget::eventFilter( QObject *obj, QEvent *event )
1945 return QgsDockWidget::eventFilter( obj, event );
1953 if ( event->type() == QEvent::ShortcutOverride || event->type() == QEvent::KeyPress )
1955 if ( QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( event ) )
1957 return filterKeyPress( keyEvent );
1960 return QgsDockWidget::eventFilter( obj, event );
1963bool QgsAdvancedDigitizingDockWidget::filterKeyPress( QKeyEvent *e )
1969 const QEvent::Type type = e->type();
1972 case Qt::Key_Escape:
1974 if ( type == QEvent::KeyPress && mCurrentTool )
1976 mCurrentTool->deleteLater();
1978 else if ( type == QEvent::KeyPress && mConstructionMode && mConstructionGuideLine.numPoints() >= 2 )
1980 mConstructionGuidesLayer->dataProvider()->deleteFeatures(
QgsFeatureIds() << mConstructionGuideId );
1981 mConstructionGuideLine.clear();
1983 if ( mCadPointList.size() > 1 )
1985 mConstructionGuideLine.addVertex( mCadPointList.at( 1 ) );
2000 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
2002 mXConstraint->toggleLocked();
2007 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
2011 mXConstraint->toggleRelative();
2018 else if ( type == QEvent::KeyPress )
2020 mXLineEdit->setFocus();
2021 mXLineEdit->selectAll();
2030 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
2032 mYConstraint->toggleLocked();
2037 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
2041 mYConstraint->toggleRelative();
2048 else if ( type == QEvent::KeyPress )
2050 mYLineEdit->setFocus();
2051 mYLineEdit->selectAll();
2060 if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::AltModifier )
2062 mZConstraint->toggleLocked();
2067 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
2071 mZConstraint->toggleRelative();
2078 else if ( type == QEvent::KeyPress )
2080 mZLineEdit->setFocus();
2081 mZLineEdit->selectAll();
2090 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
2092 mMConstraint->toggleLocked();
2097 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
2101 mMConstraint->toggleRelative();
2108 else if ( type == QEvent::KeyPress )
2110 mMLineEdit->setFocus();
2111 mMLineEdit->selectAll();
2120 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
2124 mAngleConstraint->toggleLocked();
2130 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
2134 mAngleConstraint->toggleRelative();
2141 else if ( type == QEvent::KeyPress )
2143 mAngleLineEdit->setFocus();
2144 mAngleLineEdit->selectAll();
2153 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
2162 else if ( type == QEvent::KeyPress )
2164 mDistanceLineEdit->setFocus();
2165 mDistanceLineEdit->selectAll();
2173 if ( type == QEvent::KeyPress )
2175 setConstructionMode( !mConstructionMode );
2182 if ( type == QEvent::KeyPress )
2184 const bool parallel = mParallelAction->isChecked();
2185 const bool perpendicular = mPerpendicularAction->isChecked();
2187 if ( !parallel && !perpendicular )
2191 else if ( perpendicular )
2208 if ( type == QEvent::ShortcutOverride )
2210 const QList<double> constActionKeys { mCommonAngleActions.keys() };
2211 const int currentAngleActionIndex {
static_cast<int>( constActionKeys.indexOf( mCommonAngleConstraint ) ) };
2212 const QList<QAction *> constActions { mCommonAngleActions.values() };
2213 QAction *nextAngleAction;
2214 if ( e->modifiers() == Qt::ShiftModifier )
2216 nextAngleAction = currentAngleActionIndex == 0 ? constActions.last() : constActions.at( currentAngleActionIndex - 1 );
2220 nextAngleAction = currentAngleActionIndex == constActions.count() - 1 ? constActions.first() : constActions.at( currentAngleActionIndex + 1 );
2222 nextAngleAction->trigger();
2232 return e->isAccepted();
2239 if ( mMapCanvas->mapSettings().destinationCrs().isGeographic() )
2241 mAngleLineEdit->setToolTip( tr(
"Angle constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
2242 mDistanceLineEdit->setToolTip( tr(
"Distance constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
2244 mLabelX->setText( tr(
"Long" ) );
2245 mLabelY->setText( tr(
"Lat" ) );
2247 mXConstraint->setPrecision( 8 );
2248 mYConstraint->setPrecision( 8 );
2252 mAngleLineEdit->setToolTip(
"<b>" + tr(
"Angle" ) +
"</b><br>(" + tr(
"press a for quick access" ) +
")" );
2253 mAngleLineEdit->setToolTip( QString() );
2255 mDistanceLineEdit->setToolTip(
"<b>" + tr(
"Distance" ) +
"</b><br>(" + tr(
"press d for quick access" ) +
")" );
2257 mLabelX->setText( tr(
"x" ) );
2258 mLabelY->setText( tr(
"y" ) );
2260 mXConstraint->setPrecision( 6 );
2261 mYConstraint->setPrecision( 6 );
2266 mEnableAction->setEnabled(
true );
2267 mErrorLabel->hide();
2270 mCurrentMapToolSupportsCad =
true;
2272 if ( mSessionActive && !isVisible() )
2277 setCadEnabled( mSessionActive );
2279 if ( !mConstructionGuidesLayer )
2281 resetConstructionGuides();
2284 if ( mDeferredUpdateConstructionGuidesCrs )
2286 updateConstructionGuidesCrs();
2296 mEnableAction->setEnabled(
false );
2297 mErrorLabel->setText( tr(
"Advanced digitizing tools are not enabled for the current map tool" ) );
2298 mErrorLabel->show();
2301 mCurrentMapToolSupportsCad =
false;
2303 mSnapIndicator->setVisible(
false );
2305 setCadEnabled(
false );
2310 mCadPaintItem->update();
2315 if ( !force && ( mLineExtensionConstraint->isLocked() || mXyVertexConstraint->isLocked() ) )
2320 mLockedSnapVertices.clear();
2325 QgsPoint pt = pointXYToPoint( point );
2328 mCadPointList << pt;
2332 mCadPointList.insert( 0, pt );
2339 mConstructionGuideLine.addVertex( pt );
2341 if ( mConstructionGuideLine.numPoints() == 2 )
2344 QgsGeometry geom( mConstructionGuideLine.clone() );
2346 ( void ) mConstructionGuidesLayer->dataProvider()->addFeature( feature );
2347 mConstructionGuideId = feature.
id();
2349 else if ( mConstructionGuideLine.numPoints() > 2 )
2351 QgsGeometry geom( mConstructionGuideLine.clone() );
2352 ( void ) mConstructionGuidesLayer->dataProvider()->changeGeometryValues( { { mConstructionGuideId, geom } } );
2357 if ( !mConstructionGuideLine.isEmpty() )
2359 mConstructionGuideLine.addVertex( pt );
2361 QgsGeometry geom( mConstructionGuideLine.clone() );
2362 ( void ) mConstructionGuidesLayer->dataProvider()->changeGeometryValues( { { mConstructionGuideId, geom } } );
2363 mConstructionGuideLine.clear();
2378 mCadPointList.removeAt( i );
2385 mCadPointList.clear();
2386 mSnappedSegment.clear();
2396 mCadPointList << point;
2401 mCadPointList[0] = point;
2408 if ( mode == mLockMode )
2413 mLockerButton->setChecked( mode ==
HardLock );
2414 if ( mRepeatingLockButton )
2418 mRepeatingLockButton->setEnabled(
true );
2422 mRepeatingLockButton->setChecked(
false );
2423 mRepeatingLockButton->setEnabled(
false );
2435 mRepeatingLock = repeating;
2436 if ( mRepeatingLockButton )
2437 mRepeatingLockButton->setChecked( repeating );
2443 if ( mRelativeButton )
2445 mRelativeButton->setChecked(
relative );
2452 if ( updateWidget && mLineEdit->isEnabled() )
2458 switch ( mCadConstraintType )
2462 return QLocale().toString( mValue,
'f', mPrecision ).append( tr(
" °" ) );
2467 if ( mMapCanvas->mapSettings().destinationCrs().isGeographic() )
2469 return QLocale().toString( mValue,
'f', mPrecision ).append( tr(
" °" ) );
2473 return QLocale().toString( mValue,
'f', mPrecision );
2490 return QLocale().toString( mValue,
'f', mPrecision );
2506 if ( mLineEdit->isEnabled() )
2512 return mCadConstraintType;
2517 mCadConstraintType = constraintType;
2522 mMapCanvas = mapCanvas;
2527 QString
value { text.trimmed() };
2528 switch ( constraintType )
2534 if (
value.endsWith( distanceUnit ) )
2536 value.chop( distanceUnit.length() );
2543 const QString angleUnit { tr(
"°" ) };
2544 if (
value.endsWith( angleUnit ) )
2546 value.chop( angleUnit.length() );
2553 return value.trimmed();
2561 return mCadPointList.value( 0 );
2570 QgsPoint res = mCadPointList.value( 0 );
2571 const QgsPointXY layerCoordinates = mMapCanvas->mapSettings().mapToLayerCoordinates( layer, res );
2572 res.
setX( layerCoordinates.
x() );
2573 res.
setY( layerCoordinates.
y() );
2584 return mCadPointList.value( 1 );
2594 return mCadPointList.value( 2 );
2599QgsPoint QgsAdvancedDigitizingDockWidget::pointXYToPoint(
const QgsPointXY &point )
const
2606 return mZLineEdit->isEnabled() ? QLocale().toDouble( mZLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
2611 return mMLineEdit->isEnabled() ? QLocale().toDouble( mMLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
2616 mWeightValue = value;
2617 const bool wasEnabled = mWeightEnabled;
2618 mWeightEnabled = enabled;
2621 if ( wasEnabled != enabled )
2629 return mWeightValue;
2634 return mShowConstructionGuides ? mShowConstructionGuides->isChecked() :
false;
2639 return mSnapToConstructionGuides ? mShowConstructionGuides->isChecked() && mSnapToConstructionGuides->isChecked() :
false;
2644 return mRecordConstructionGuides ? mRecordConstructionGuides->isChecked() :
false;
2647void QgsAdvancedDigitizingDockWidget::updateConstructionGuidesCrs()
2649 if ( !mConstructionGuidesLayer )
2656 mDeferredUpdateConstructionGuidesCrs =
true;
2659 QgsCoordinateTransform transform = QgsCoordinateTransform( mConstructionGuidesLayer->crs(), mMapCanvas->mapSettings().destinationCrs(),
QgsProject::instance()->
transformContext() );
2660 mConstructionGuidesLayer->setCrs( mMapCanvas->mapSettings().destinationCrs() );
2661 QgsFeatureIterator it = mConstructionGuidesLayer->getFeatures( QgsFeatureRequest().setNoAttributes() );
2665 QgsGeometry geom = feature.
geometry();
2667 mConstructionGuidesLayer->dataProvider()->changeGeometryValues( { { feature.
id(), geom } } );
2670 mDeferredUpdateConstructionGuidesCrs =
false;
2673void QgsAdvancedDigitizingDockWidget::resetConstructionGuides()
2675 if ( mConstructionGuidesLayer )
2677 mConstructionGuidesLayer.reset();
2680 const QgsVectorLayer::LayerOptions options(
QgsProject::instance()->transformContext(),
false,
false );
2681 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
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.