18#include <QCoreApplication>
43#include <QActionGroup>
48 , mMapCanvas( canvas )
50 , mCommonAngleConstraint(
QgsSettings().value( QStringLiteral(
"/Cad/CommonAngle" ), 0.0 ).toDouble() )
56 mAngleConstraint.reset(
new CadConstraint( mAngleLineEdit, mLockAngleButton, mRelativeAngleButton, mRepeatingLockAngleButton ) );
57 mDistanceConstraint.reset(
new CadConstraint( mDistanceLineEdit, mLockDistanceButton,
nullptr, mRepeatingLockDistanceButton ) );
58 mXConstraint.reset(
new CadConstraint( mXLineEdit, mLockXButton, mRelativeXButton, mRepeatingLockXButton ) );
59 mYConstraint.reset(
new CadConstraint( mYLineEdit, mLockYButton, mRelativeYButton, mRepeatingLockYButton ) );
60 mZConstraint.reset(
new CadConstraint( mZLineEdit, mLockZButton, mRelativeZButton, mRepeatingLockZButton ) );
61 mMConstraint.reset(
new CadConstraint( mMLineEdit, mLockMButton, mRelativeMButton, mRepeatingLockMButton ) );
63 mLineExtensionConstraint.reset(
new CadConstraint(
new QLineEdit(),
new QToolButton() ) );
64 mXyVertexConstraint.reset(
new CadConstraint(
new QLineEdit(),
new QToolButton() ) );
68 mMapCanvas->installEventFilter(
this );
69 mAngleLineEdit->installEventFilter(
this );
70 mDistanceLineEdit->installEventFilter(
this );
71 mXLineEdit->installEventFilter(
this );
72 mYLineEdit->installEventFilter(
this );
73 mZLineEdit->installEventFilter(
this );
74 mMLineEdit->installEventFilter(
this );
77 connect( mEnableAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::activateCad );
78 connect( mConstructionModeAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::setConstructionMode );
79 connect( mParallelAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
80 connect( mPerpendicularAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
81 connect( mLockAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
82 connect( mLockDistanceButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
83 connect( mLockXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
84 connect( mLockYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
85 connect( mLockZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
86 connect( mLockMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
87 connect( mRelativeAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
88 connect( mRelativeXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
89 connect( mRelativeYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
90 connect( mRelativeZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
91 connect( mRelativeMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
92 connect( mRepeatingLockDistanceButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
93 connect( mRepeatingLockAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
94 connect( mRepeatingLockXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
95 connect( mRepeatingLockYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
96 connect( mRepeatingLockZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
97 connect( mRepeatingLockMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
98 connect( mAngleLineEdit, &QLineEdit::returnPressed,
this, [ = ]() { lockConstraint(); } );
99 connect( mDistanceLineEdit, &QLineEdit::returnPressed,
this, [ = ]() { lockConstraint(); } );
100 connect( mXLineEdit, &QLineEdit::returnPressed,
this, [ = ]() { lockConstraint(); } );
101 connect( mYLineEdit, &QLineEdit::returnPressed,
this, [ = ]() { lockConstraint(); } );
102 connect( mZLineEdit, &QLineEdit::returnPressed,
this, [ = ]() { lockConstraint(); } );
103 connect( mMLineEdit, &QLineEdit::returnPressed,
this, [ = ]() { lockConstraint(); } );
104 connect( mAngleLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
105 connect( mDistanceLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
106 connect( mXLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
107 connect( mYLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
108 connect( mZLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
109 connect( mMLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
125 QMenu *menu =
new QMenu(
this );
127 QActionGroup *angleButtonGroup =
new QActionGroup( menu );
128 mCommonAngleActions = QMap<QAction *, double>();
129 QList< QPair< double, QString > > commonAngles;
131 const QList<double> anglesDouble( { 0.0, 5.0, 10.0, 15.0, 18.0, 22.5, 30.0, 45.0, 90.0} );
132 for ( QList<double>::const_iterator it = anglesDouble.constBegin(); it != anglesDouble.constEnd(); ++it )
135 menuText = tr(
"Do Not Snap to Common Angles" );
137 menuText = QString( tr(
"%1, %2, %3, %4°…" ) ).arg( *it, 0,
'f', 1 ).arg( *it * 2, 0,
'f', 1 ).arg( *it * 3, 0,
'f', 1 ).arg( *it * 4, 0,
'f', 1 );
138 commonAngles << QPair<double, QString>( *it, menuText );
140 for ( QList< QPair<double, QString > >::const_iterator it = commonAngles.constBegin(); it != commonAngles.constEnd(); ++it )
142 QAction *action =
new QAction( it->second, menu );
143 action->setCheckable(
true );
144 action->setChecked( it->first == mCommonAngleConstraint );
145 menu->addAction( action );
146 angleButtonGroup->addAction( action );
147 mCommonAngleActions.insert( action, it->first );
150 qobject_cast< QToolButton *>( mToolbar->widgetForAction( mSettingsAction ) )->setPopupMode( QToolButton::InstantPopup );
151 mSettingsAction->setMenu( menu );
152 mSettingsAction->setCheckable(
true );
153 mSettingsAction->setToolTip( tr(
"Snap to common angles" ) );
154 mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
155 connect( menu, &QMenu::triggered,
this, &QgsAdvancedDigitizingDockWidget::settingsButtonTriggered );
158 QMenu *constructionMenu =
new QMenu(
this );
160 mLineExtensionAction =
new QAction(
"Line Extension", constructionMenu );
161 mLineExtensionAction->setCheckable(
true );
162 constructionMenu->addAction( mLineExtensionAction );
163 connect( mLineExtensionAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
165 mXyVertexAction =
new QAction(
"X/Y Point", constructionMenu );
166 mXyVertexAction->setCheckable(
true );
167 constructionMenu->addAction( mXyVertexAction );
168 connect( mXyVertexAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
170 auto constructionToolBar = qobject_cast< QToolButton *>( mToolbar->widgetForAction( mConstructionAction ) );
171 constructionToolBar->setPopupMode( QToolButton::InstantPopup );
172 constructionToolBar->setMenu( constructionMenu );
173 constructionToolBar->setObjectName( QStringLiteral(
"ConstructionButton" ) );
175 mConstructionAction->setMenu( menu );
176 mConstructionAction->setCheckable(
true );
177 mConstructionAction->setToolTip( tr(
"Construction Tools" ) );
181 mConstructionModeAction->setToolTip(
"<b>" + tr(
"Construction mode" ) +
"</b><br>(" + tr(
"press c to toggle on/off" ) +
")" );
182 mDistanceLineEdit->setToolTip(
"<b>" + tr(
"Distance" ) +
"</b><br>(" + tr(
"press d for quick access" ) +
")" );
183 mLockDistanceButton->setToolTip(
"<b>" + tr(
"Lock distance" ) +
"</b><br>(" + tr(
"press Ctrl + d for quick access" ) +
")" );
184 mRepeatingLockDistanceButton->setToolTip(
"<b>" + tr(
"Continuously lock distance" ) +
"</b>" );
186 mRelativeAngleButton->setToolTip(
"<b>" + tr(
"Toggles relative angle to previous segment" ) +
"</b><br>(" + tr(
"press Shift + a for quick access" ) +
")" );
187 mAngleLineEdit->setToolTip(
"<b>" + tr(
"Angle" ) +
"</b><br>(" + tr(
"press a for quick access" ) +
")" );
188 mLockAngleButton->setToolTip(
"<b>" + tr(
"Lock angle" ) +
"</b><br>(" + tr(
"press Ctrl + a for quick access" ) +
")" );
189 mRepeatingLockAngleButton->setToolTip(
"<b>" + tr(
"Continuously lock angle" ) +
"</b>" );
191 mRelativeXButton->setToolTip(
"<b>" + tr(
"Toggles relative x to previous node" ) +
"</b><br>(" + tr(
"press Shift + x for quick access" ) +
")" );
192 mXLineEdit->setToolTip(
"<b>" + tr(
"X coordinate" ) +
"</b><br>(" + tr(
"press x for quick access" ) +
")" );
193 mLockXButton->setToolTip(
"<b>" + tr(
"Lock x coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + x for quick access" ) +
")" );
194 mRepeatingLockXButton->setToolTip(
"<b>" + tr(
"Continuously lock x coordinate" ) +
"</b>" );
196 mRelativeYButton->setToolTip(
"<b>" + tr(
"Toggles relative y to previous node" ) +
"</b><br>(" + tr(
"press Shift + y for quick access" ) +
")" );
197 mYLineEdit->setToolTip(
"<b>" + tr(
"Y coordinate" ) +
"</b><br>(" + tr(
"press y for quick access" ) +
")" );
198 mLockYButton->setToolTip(
"<b>" + tr(
"Lock y coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + y for quick access" ) +
")" );
199 mRepeatingLockYButton->setToolTip(
"<b>" + tr(
"Continuously lock y coordinate" ) +
"</b>" );
201 mRelativeZButton->setToolTip(
"<b>" + tr(
"Toggles relative z to previous node" ) +
"</b><br>(" + tr(
"press Shift + z for quick access" ) +
")" );
202 mZLineEdit->setToolTip(
"<b>" + tr(
"Z coordinate" ) +
"</b><br>(" + tr(
"press z for quick access" ) +
")" );
203 mLockZButton->setToolTip(
"<b>" + tr(
"Lock z coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + z for quick access" ) +
")" );
204 mRepeatingLockZButton->setToolTip(
"<b>" + tr(
"Continuously lock z coordinate" ) +
"</b>" );
206 mRelativeMButton->setToolTip(
"<b>" + tr(
"Toggles relative m to previous node" ) +
"</b><br>(" + tr(
"press Shift + m for quick access" ) +
")" );
207 mMLineEdit->setToolTip(
"<b>" + tr(
"M coordinate" ) +
"</b><br>(" + tr(
"press m for quick access" ) +
")" );
208 mLockMButton->setToolTip(
"<b>" + tr(
"Lock m coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + m for quick access" ) +
")" );
209 mRepeatingLockMButton->setToolTip(
"<b>" + tr(
"Continuously lock m coordinate" ) +
"</b>" );
222 mToggleFloaterAction->setChecked( mFloater->
active() );
224 updateCapacity(
true );
232 mXLineEdit->setText( value );
233 if ( mode == WidgetSetMode::ReturnPressed )
235 emit mXLineEdit->returnPressed();
237 else if ( mode == WidgetSetMode::FocusOut )
239 QEvent *e =
new QEvent( QEvent::FocusOut );
240 QCoreApplication::postEvent( mXLineEdit, e );
242 else if ( mode == WidgetSetMode::TextEdited )
244 emit mXLineEdit->textEdited( value );
249 mYLineEdit->setText( value );
250 if ( mode == WidgetSetMode::ReturnPressed )
252 emit mYLineEdit->returnPressed();
254 else if ( mode == WidgetSetMode::FocusOut )
256 QEvent *e =
new QEvent( QEvent::FocusOut );
257 QCoreApplication::postEvent( mYLineEdit, e );
259 else if ( mode == WidgetSetMode::TextEdited )
261 emit mYLineEdit->textEdited( value );
266 mZLineEdit->setText( value );
267 if ( mode == WidgetSetMode::ReturnPressed )
269 emit mZLineEdit->returnPressed();
271 else if ( mode == WidgetSetMode::FocusOut )
273 QEvent *e =
new QEvent( QEvent::FocusOut );
274 QCoreApplication::postEvent( mZLineEdit, e );
276 else if ( mode == WidgetSetMode::TextEdited )
278 emit mZLineEdit->textEdited( value );
283 mMLineEdit->setText( value );
284 if ( mode == WidgetSetMode::ReturnPressed )
286 emit mMLineEdit->returnPressed();
288 else if ( mode == WidgetSetMode::FocusOut )
290 QEvent *e =
new QEvent( QEvent::FocusOut );
291 QCoreApplication::postEvent( mMLineEdit, e );
293 else if ( mode == WidgetSetMode::TextEdited )
295 emit mMLineEdit->textEdited( value );
300 mAngleLineEdit->setText( value );
301 if ( mode == WidgetSetMode::ReturnPressed )
303 emit mAngleLineEdit->returnPressed();
305 else if ( mode == WidgetSetMode::FocusOut )
307 emit mAngleLineEdit->textEdited( value );
312 mDistanceLineEdit->setText( value );
313 if ( mode == WidgetSetMode::ReturnPressed )
315 emit mDistanceLineEdit->returnPressed();
317 else if ( mode == WidgetSetMode::FocusOut )
319 QEvent *e =
new QEvent( QEvent::FocusOut );
320 QCoreApplication::postEvent( mDistanceLineEdit, e );
322 else if ( mode == WidgetSetMode::TextEdited )
324 emit mDistanceLineEdit->textEdited( value );
329void QgsAdvancedDigitizingDockWidget::setCadEnabled(
bool enabled )
331 mCadEnabled = enabled;
332 mEnableAction->setChecked( enabled );
333 mConstructionModeAction->setEnabled( enabled );
334 mSettingsAction->setEnabled( enabled );
335 mInputWidgets->setEnabled( enabled );
336 mToggleFloaterAction->setEnabled( enabled );
337 mConstructionAction->setEnabled( enabled );
342 mLineExtensionAction->setChecked(
false );
343 mXyVertexAction->setChecked(
false );
345 mParallelAction->setEnabled(
false );
346 mPerpendicularAction->setEnabled(
false );
352 setConstructionMode(
false );
363 bool enableZ =
false;
364 bool enableM =
false;
368 switch ( layer->type() )
381 QgsMeshLayer *mlayer = qobject_cast<QgsMeshLayer *>( layer );
402 mRelativeZButton->setEnabled(
enable );
403 mZLabel->setEnabled(
enable );
404 mZLineEdit->setEnabled(
enable );
405 if ( mZLineEdit->isEnabled() )
409 mLockZButton->setEnabled(
enable );
415 mRelativeMButton->setEnabled(
enable );
416 mMLabel->setEnabled(
enable );
417 mMLineEdit->setEnabled(
enable );
418 if ( mMLineEdit->isEnabled() )
422 mLockMButton->setEnabled(
enable );
426void QgsAdvancedDigitizingDockWidget::activateCad(
bool enabled )
428 enabled &= mCurrentMapToolSupportsCad;
430 mSessionActive = enabled;
432 if ( enabled && !isVisible() )
437 setCadEnabled( enabled );
440void QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked(
bool activated )
446 else if ( sender() == mParallelAction )
450 else if ( sender() == mPerpendicularAction )
456void QgsAdvancedDigitizingDockWidget::setConstraintRelative(
bool activate )
458 if ( sender() == mRelativeAngleButton )
460 mAngleConstraint->setRelative( activate );
463 else if ( sender() == mRelativeXButton )
465 mXConstraint->setRelative( activate );
468 else if ( sender() == mRelativeYButton )
470 mYConstraint->setRelative( activate );
473 else if ( sender() == mRelativeZButton )
475 mZConstraint->setRelative( activate );
478 else if ( sender() == mRelativeMButton )
480 mMConstraint->setRelative( activate );
485void QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock(
bool activate )
487 if ( sender() == mRepeatingLockDistanceButton )
489 mDistanceConstraint->setRepeatingLock( activate );
491 else if ( sender() == mRepeatingLockAngleButton )
493 mAngleConstraint->setRepeatingLock( activate );
495 else if ( sender() == mRepeatingLockXButton )
497 mXConstraint->setRepeatingLock( activate );
499 else if ( sender() == mRepeatingLockYButton )
501 mYConstraint->setRepeatingLock( activate );
503 else if ( sender() == mRepeatingLockZButton )
505 mZConstraint->setRepeatingLock( activate );
507 else if ( sender() == mRepeatingLockMButton )
509 mMConstraint->setRepeatingLock( activate );
513void QgsAdvancedDigitizingDockWidget::setConstructionMode(
bool enabled )
515 mConstructionMode = enabled;
516 mConstructionModeAction->setChecked( enabled );
519void QgsAdvancedDigitizingDockWidget::settingsButtonTriggered( QAction *action )
522 const QMap<QAction *, double>::const_iterator ica = mCommonAngleActions.constFind( action );
523 if ( ica != mCommonAngleActions.constEnd() )
525 ica.key()->setChecked(
true );
526 mCommonAngleConstraint = ica.value();
528 mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
533QgsMapLayer *QgsAdvancedDigitizingDockWidget::targetLayer()
const
537 return advancedTool->layer();
551 if ( releaseRepeatingLocks )
553 mXyVertexAction->setChecked(
false );
557 mLineExtensionAction->setChecked(
false );
564 if ( releaseRepeatingLocks || !mAngleConstraint->isRepeatingLock() )
569 if ( releaseRepeatingLocks || !mDistanceConstraint->isRepeatingLock() )
574 if ( releaseRepeatingLocks || !mXConstraint->isRepeatingLock() )
579 if ( releaseRepeatingLocks || !mYConstraint->isRepeatingLock() )
584 if ( releaseRepeatingLocks || !mZConstraint->isRepeatingLock() )
589 if ( releaseRepeatingLocks || !mMConstraint->isRepeatingLock() )
595 if ( !mCadPointList.empty() )
597 if ( !mXConstraint->isLocked() && !mXConstraint->relative() )
599 mXConstraint->setValue( mCadPointList.constLast().x(),
true );
601 if ( !mYConstraint->isLocked() && !mYConstraint->relative() )
603 mYConstraint->setValue( mCadPointList.constLast().y(),
true );
605 if ( !mZConstraint->isLocked() && !mZConstraint->relative() )
607 mZConstraint->setValue( mCadPointList.constLast().z(),
true );
609 if ( !mMConstraint->isLocked() && !mMConstraint->relative() )
611 mMConstraint->setValue( mCadPointList.constLast().m(),
true );
618void QgsAdvancedDigitizingDockWidget::emit pointChanged()
621 QPoint globalPos = mMapCanvas->cursor().pos();
622 QPoint pos = mMapCanvas->mapFromGlobal( globalPos );
623 QMouseEvent *e =
new QMouseEvent( QEvent::MouseMove, pos, globalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier );
624 mCurrentMapTool->canvasMoveEvent( e );
630 CadConstraint *constraint =
nullptr;
631 if ( obj == mAngleLineEdit || obj == mLockAngleButton )
633 constraint = mAngleConstraint.get();
635 else if ( obj == mDistanceLineEdit || obj == mLockDistanceButton )
637 constraint = mDistanceConstraint.get();
639 else if ( obj == mXLineEdit || obj == mLockXButton )
641 constraint = mXConstraint.get();
643 else if ( obj == mYLineEdit || obj == mLockYButton )
645 constraint = mYConstraint.get();
647 else if ( obj == mZLineEdit || obj == mLockZButton )
649 constraint = mZConstraint.get();
651 else if ( obj == mMLineEdit || obj == mLockMButton )
653 constraint = mMConstraint.get();
655 else if ( obj == mLineExtensionAction )
657 constraint = mLineExtensionConstraint.get();
659 else if ( obj == mXyVertexAction )
661 constraint = mXyVertexConstraint.get();
666double QgsAdvancedDigitizingDockWidget::parseUserInput(
const QString &inputValue,
bool &ok )
const
678 const QVariant result = expr.evaluate();
679 if ( expr.hasEvalError() )
682 QString inputValueC { inputValue };
685 if ( inputValue.contains( QLocale().groupSeparator() ) )
687 inputValueC.remove( QLocale().groupSeparator() );
689 const QVariant resultC = exprC.evaluate();
690 if ( ! exprC.hasEvalError() )
692 value = resultC.toDouble( &ok );
697 if ( !ok && QLocale().decimalPoint() != QChar(
'.' ) && inputValueC.contains( QLocale().decimalPoint() ) )
699 QgsExpression exprC( inputValueC .replace( QLocale().decimalPoint(), QChar(
'.' ) ) );
700 const QVariant resultC = exprC.evaluate();
701 if ( ! exprC.hasEvalError() )
703 value = resultC.toDouble( &ok );
709 value = result.toDouble( &ok );
715void QgsAdvancedDigitizingDockWidget::updateConstraintValue( CadConstraint *constraint,
const QString &textValue,
bool convertExpression )
717 if ( !constraint || textValue.isEmpty() )
726 const double value = parseUserInput( textValue, ok );
730 constraint->setValue( value, convertExpression );
735void QgsAdvancedDigitizingDockWidget::lockConstraint(
bool activate )
737 CadConstraint *constraint = objectToConstraint( sender() );
745 const QString textValue = constraint->lineEdit()->text();
746 if ( !textValue.isEmpty() )
749 const double value = parseUserInput( textValue, ok );
752 constraint->setValue( value );
766 if ( constraint == mXConstraint.get() )
770 else if ( constraint == mYConstraint.get() )
774 else if ( constraint == mZConstraint.get() )
778 else if ( constraint == mMConstraint.get() )
782 else if ( constraint == mDistanceConstraint.get() )
786 else if ( constraint == mAngleConstraint.get() )
794 if ( constraint == mAngleConstraint.get() )
804void QgsAdvancedDigitizingDockWidget::constraintTextEdited(
const QString &textValue )
806 CadConstraint *constraint = objectToConstraint( sender() );
812 updateConstraintValue( constraint, textValue,
false );
815void QgsAdvancedDigitizingDockWidget::constraintFocusOut()
817 QLineEdit *lineEdit = qobject_cast< QLineEdit * >( sender()->parent() );
821 CadConstraint *constraint = objectToConstraint( lineEdit );
827 updateConstraintValue( constraint, lineEdit->text(),
true );
832 mBetweenLineConstraint = constraint;
837void QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint(
bool activate )
839 CadConstraint *constraint = objectToConstraint( sender() );
847 if ( constraint == mXyVertexConstraint.get() )
851 else if ( constraint == mLineExtensionConstraint.get() )
865void QgsAdvancedDigitizingDockWidget::updateCapacity(
bool updateUIwithoutChange )
867 CadCapacities newCapacities = CadCapacities();
871 if ( mCadPointList.count() > 1 )
880 if ( mCadPointList.count() > 2 )
885 if ( !updateUIwithoutChange && newCapacities == mCapacities )
895 const bool distance = mCadEnabled && newCapacities.testFlag(
Distance );
896 const bool relativeAngle = mCadEnabled && newCapacities.testFlag(
RelativeAngle );
897 const bool absoluteAngle = mCadEnabled && newCapacities.testFlag(
AbsoluteAngle );
898 const bool relativeCoordinates = mCadEnabled && newCapacities.testFlag(
RelativeCoordinates );
900 mPerpendicularAction->setEnabled( distance && snappingEnabled );
901 mParallelAction->setEnabled( distance && snappingEnabled );
903 mLineExtensionAction->setEnabled( snappingEnabled );
904 mXyVertexAction->setEnabled( snappingEnabled );
908 if ( !snappingEnabled )
910 mPerpendicularAction->setToolTip( tr(
"Snapping must be enabled to utilize perpendicular mode." ) );
911 mParallelAction->setToolTip( tr(
"Snapping must be enabled to utilize parallel mode." ) );
912 mLineExtensionAction->setToolTip( tr(
"Snapping must be enabled to utilize line extension mode." ) );
913 mXyVertexAction->setToolTip( tr(
"Snapping must be enabled to utilize xy point mode." ) );
915 else if ( mCadPointList.count() <= 1 )
917 mPerpendicularAction->setToolTip( tr(
"A first vertex should be drawn to utilize perpendicular mode." ) );
918 mParallelAction->setToolTip( tr(
"A first vertex should be drawn to utilize parallel mode." ) );
920 else if ( isGeographic )
922 mPerpendicularAction->setToolTip( tr(
"Perpendicular mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
923 mParallelAction->setToolTip( tr(
"Parallel mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
927 mPerpendicularAction->setToolTip(
"<b>" + tr(
"Perpendicular" ) +
"</b><br>(" + tr(
"press p to switch between perpendicular, parallel and normal mode" ) +
")" );
928 mParallelAction->setToolTip(
"<b>" + tr(
"Parallel" ) +
"</b><br>(" + tr(
"press p to switch between perpendicular, parallel and normal mode" ) +
")" );
932 if ( !absoluteAngle )
938 mLockAngleButton->setEnabled( absoluteAngle );
939 mRelativeAngleButton->setEnabled( relativeAngle );
940 mAngleLineEdit->setEnabled( absoluteAngle );
942 if ( !absoluteAngle )
946 if ( !relativeAngle )
948 mAngleConstraint->setRelative(
false );
951 else if ( relativeAngle && !mCapacities.testFlag(
RelativeAngle ) )
954 mAngleConstraint->setRelative(
true );
959 mLockDistanceButton->setEnabled( distance && relativeCoordinates );
960 mDistanceLineEdit->setEnabled( distance && relativeCoordinates );
962 if ( !( distance && relativeCoordinates ) )
967 mRelativeXButton->setEnabled( relativeCoordinates );
968 mRelativeYButton->setEnabled( relativeCoordinates );
969 mRelativeZButton->setEnabled( relativeCoordinates );
970 mRelativeMButton->setEnabled( relativeCoordinates );
973 mCapacities = newCapacities;
983 constr.
value =
c->value();
990 if ( !mLineExtensionConstraint->isLocked() && !mXyVertexConstraint->isLocked() )
996 const int lastIndex = mLockedSnapVertices.length() - 1;
997 for (
int i = lastIndex ; i >= 0; --i )
999 if ( mLockedSnapVertices[i].point() == snapMatch.
point() )
1001 if ( snapMatch.
point() != previouslySnap.
point() )
1003 mLockedSnapVertices.removeAt( i );
1009 if ( snapMatch.
point() != previouslySnap.
point() )
1011 mLockedSnapVertices.enqueue( snapMatch );
1014 if ( mLockedSnapVertices.count() > 3 )
1016 mLockedSnapVertices.dequeue();
1025 context.
xConstraint = _constraint( mXConstraint.get() );
1026 context.
yConstraint = _constraint( mYConstraint.get() );
1027 context.
zConstraint = _constraint( mZConstraint.get() );
1028 context.
mConstraint = _constraint( mMConstraint.get() );
1044 const bool res = output.
valid;
1046 mSnappedSegment.clear();
1051 mSnappedSegment << edgePt0 << edgePt1;
1072 mSnapIndicator->setMatch( output.
snapMatch );
1073 mSnapIndicator->setVisible(
true );
1077 mSnapIndicator->setVisible(
false );
1098 if ( mSnapMatch.
layer() )
1110 toggleLockedSnapVertex( mSnapMatch, mLastSnapMatch );
1111 mLastSnapMatch = mSnapMatch;
1121 if ( mLockZButton->isChecked() )
1123 point.
setZ( QLocale().toDouble( mZLineEdit->text() ) );
1125 if ( mLockMButton->isChecked() )
1127 point.
setM( QLocale().toDouble( mMLineEdit->text() ) );
1131 updateCurrentPoint( point );
1133 updateUnlockedConstraintValues( point );
1141 emit
pushWarning( tr(
"Some constraints are incompatible. Resulting point might be incorrect." ) );
1148void QgsAdvancedDigitizingDockWidget::updateUnlockedConstraintValues(
const QgsPoint &point )
1150 bool previousPointExist, penulPointExist;
1155 if ( !mAngleConstraint->isLocked() && previousPointExist )
1158 if ( penulPointExist && mAngleConstraint->relative() )
1161 angle = std::atan2( previousPt.
y() - penultimatePt.
y(),
1162 previousPt.
x() - penultimatePt.
x() );
1164 angle = ( std::atan2( point.
y() - previousPt.
y(),
1165 point.
x() - previousPt.
x()
1166 ) -
angle ) * 180 / M_PI;
1169 mAngleConstraint->setValue(
angle );
1172 if ( !mDistanceConstraint->isLocked() && previousPointExist )
1174 mDistanceConstraint->setValue( std::sqrt( previousPt.
distanceSquared( point ) ) );
1177 if ( !mXConstraint->isLocked() )
1179 if ( previousPointExist && mXConstraint->relative() )
1181 mXConstraint->setValue( point.
x() - previousPt.
x() );
1185 mXConstraint->setValue( point.
x() );
1189 if ( !mYConstraint->isLocked() )
1191 if ( previousPointExist && mYConstraint->relative() )
1193 mYConstraint->setValue( point.
y() - previousPt.
y() );
1197 mYConstraint->setValue( point.
y() );
1201 if ( !mZConstraint->isLocked() )
1203 if ( previousPointExist && mZConstraint->relative() )
1205 mZConstraint->setValue( point.
z() - previousPt.
z() );
1209 mZConstraint->setValue( point.
z() );
1213 if ( !mMConstraint->isLocked() )
1215 if ( previousPointExist && mMConstraint->relative() )
1217 mMConstraint->setValue( point.
m() - previousPt.
m() );
1221 mMConstraint->setValue( point.
m() );
1227QList<QgsPointXY> QgsAdvancedDigitizingDockWidget::snapSegmentToAllLayers(
const QgsPointXY &originalMapPoint,
bool *snapped )
const
1239 localConfig.
setTypeFlag( Qgis::SnappingType::Segment );
1240 snappingUtils->
setConfig( localConfig );
1242 match = snappingUtils->
snapToMap( originalMapPoint,
nullptr,
true );
1244 snappingUtils->
setConfig( canvasConfig );
1254 *snapped =
segment.count() == 2;
1267 bool previousPointExist, penulPointExist, snappedSegmentExist;
1270 mSnappedSegment = snapSegmentToAllLayers( e->
originalMapPoint(), &snappedSegmentExist );
1272 if ( !previousPointExist || !snappedSegmentExist )
1277 double angle = std::atan2( mSnappedSegment[0].y() - mSnappedSegment[1].y(), mSnappedSegment[0].x() - mSnappedSegment[1].x() );
1279 if ( mAngleConstraint->relative() && penulPointExist )
1281 angle -= std::atan2( previousPt.
y() - penultimatePt.
y(), previousPt.
x() - penultimatePt.
x() );
1289 angle *= 180 / M_PI;
1291 mAngleConstraint->setValue(
angle );
1292 mAngleConstraint->setLockMode( lockMode );
1310 case Qt::Key_Backspace:
1311 case Qt::Key_Delete:
1317 case Qt::Key_Escape:
1347 case Qt::Key_Backspace:
1348 case Qt::Key_Delete:
1354 case Qt::Key_Escape:
1361 filterKeyPress( e );
1370 const auto constPoints = points;
1377bool QgsAdvancedDigitizingDockWidget::eventFilter( QObject *obj, QEvent *event )
1381 return QgsDockWidget::eventFilter( obj, event );
1389 if ( event->type() == QEvent::ShortcutOverride || event->type() == QEvent::KeyPress )
1391 if ( QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( event ) )
1393 return filterKeyPress( keyEvent );
1396 return QgsDockWidget::eventFilter( obj, event );
1399bool QgsAdvancedDigitizingDockWidget::filterKeyPress( QKeyEvent *e )
1405 const QEvent::Type type = e->type();
1411 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1413 mXConstraint->toggleLocked();
1418 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1422 mXConstraint->toggleRelative();
1429 else if ( type == QEvent::KeyPress )
1431 mXLineEdit->setFocus();
1432 mXLineEdit->selectAll();
1441 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1443 mYConstraint->toggleLocked();
1448 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1452 mYConstraint->toggleRelative();
1459 else if ( type == QEvent::KeyPress )
1461 mYLineEdit->setFocus();
1462 mYLineEdit->selectAll();
1471 if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::AltModifier )
1473 mZConstraint->toggleLocked();
1478 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1482 mZConstraint->toggleRelative();
1489 else if ( type == QEvent::KeyPress )
1491 mZLineEdit->setFocus();
1492 mZLineEdit->selectAll();
1501 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1503 mMConstraint->toggleLocked();
1508 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1512 mMConstraint->toggleRelative();
1519 else if ( type == QEvent::KeyPress )
1521 mMLineEdit->setFocus();
1522 mMLineEdit->selectAll();
1531 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1535 mAngleConstraint->toggleLocked();
1541 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1545 mAngleConstraint->toggleRelative();
1552 else if ( type == QEvent::KeyPress )
1554 mAngleLineEdit->setFocus();
1555 mAngleLineEdit->selectAll();
1564 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1568 mDistanceConstraint->toggleLocked();
1575 else if ( type == QEvent::KeyPress )
1577 mDistanceLineEdit->setFocus();
1578 mDistanceLineEdit->selectAll();
1586 if ( type == QEvent::KeyPress )
1588 setConstructionMode( !mConstructionMode );
1595 if ( type == QEvent::KeyPress )
1597 const bool parallel = mParallelAction->isChecked();
1598 const bool perpendicular = mPerpendicularAction->isChecked();
1600 if ( !parallel && !perpendicular )
1604 else if ( perpendicular )
1624 return e->isAccepted();
1633 mAngleLineEdit->setToolTip( tr(
"Angle constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1634 mDistanceLineEdit->setToolTip( tr(
"Distance constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1636 mLabelX->setText( tr(
"Long" ) );
1637 mLabelY->setText( tr(
"Lat" ) );
1639 mXConstraint->setPrecision( 8 );
1640 mYConstraint->setPrecision( 8 );
1644 mAngleLineEdit->setToolTip(
"<b>" + tr(
"Angle" ) +
"</b><br>(" + tr(
"press a for quick access" ) +
")" );
1645 mAngleLineEdit->setToolTip( QString() );
1647 mDistanceLineEdit->setToolTip(
"<b>" + tr(
"Distance" ) +
"</b><br>(" + tr(
"press d for quick access" ) +
")" );
1649 mLabelX->setText( tr(
"x" ) );
1650 mLabelY->setText( tr(
"y" ) );
1652 mXConstraint->setPrecision( 6 );
1653 mYConstraint->setPrecision( 6 );
1658 mEnableAction->setEnabled(
true );
1659 mErrorLabel->hide();
1662 mCurrentMapToolSupportsCad =
true;
1664 if ( mSessionActive && !isVisible() )
1668 setCadEnabled( mSessionActive );
1675 mEnableAction->setEnabled(
false );
1676 mErrorLabel->setText( tr(
"CAD tools are not enabled for the current map tool" ) );
1677 mErrorLabel->show();
1680 mCurrentMapToolSupportsCad =
false;
1682 mSnapIndicator->setVisible(
false );
1684 setCadEnabled(
false );
1689 mCadPaintItem->update();
1694 if ( !force && ( mLineExtensionConstraint->isLocked() || mXyVertexConstraint->isLocked() ) )
1699 mLockedSnapVertices.clear();
1705 QgsPoint pt = pointXYToPoint( point );
1708 mCadPointList << pt;
1712 mCadPointList.insert( 0, pt );
1725 mCadPointList.removeAt( i );
1732 mCadPointList.clear();
1733 mSnappedSegment.clear();
1739void QgsAdvancedDigitizingDockWidget::updateCurrentPoint(
const QgsPoint &point )
1743 mCadPointList << point;
1748 mCadPointList[0] = point;
1757 mLockerButton->setChecked( mode ==
HardLock );
1758 if ( mRepeatingLockButton )
1762 mRepeatingLockButton->setEnabled(
true );
1766 mRepeatingLockButton->setChecked(
false );
1767 mRepeatingLockButton->setEnabled(
false );
1780 mRepeatingLock = repeating;
1781 if ( mRepeatingLockButton )
1782 mRepeatingLockButton->setChecked( repeating );
1787 mRelative = relative;
1788 if ( mRelativeButton )
1790 mRelativeButton->setChecked( relative );
1797 if ( updateWidget && mLineEdit->isEnabled() )
1798 mLineEdit->setText( QLocale().toString( value,
'f', mPrecision ) );
1803 setLockMode( mLockMode == HardLock ? NoLock : HardLock );
1808 setRelative( !mRelative );
1814 if ( mLineEdit->isEnabled() )
1815 mLineEdit->setText( QLocale().toString( mValue,
'f', mPrecision ) );
1823 return mCadPointList.value( 0 );
1832 QgsPoint res = mCadPointList.value( 0 );
1834 res.
setX( layerCoordinates.
x() );
1835 res.
setY( layerCoordinates.
y() );
1846 return mCadPointList.value( 1 );
1856 return mCadPointList.value( 2 );
1861QgsPoint QgsAdvancedDigitizingDockWidget::pointXYToPoint(
const QgsPointXY &point )
const
1868 return mZLineEdit->isEnabled() ? QLocale().toDouble( mZLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
1873 return mMLineEdit->isEnabled() ? QLocale().toDouble( mMLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
@ AllLayers
On all vector layers.
BetweenLineConstraint
Between line constraints which can be enabled.
@ NoConstraint
No additional constraint.
@ Perpendicular
Perpendicular.
The QgsAdvancedDigitizingCanvasItem class draws the graphical elements of the CAD tools (.
void updatePosition() override
called on changed extent or resize event to update position of the item
The QgsAdvancedDigitizingFloater class is widget that floats next to the mouse pointer,...
void setActive(bool active)
Set whether the floater should be active or not.
bool active()
Whether the floater is active or not.
Structure with details of one constraint.
bool locked
Whether the constraint is active, i.e. should be considered.
double value
Numeric value of the constraint (coordinate/distance in map units or angle in degrees)
bool relative
Whether the value is relative to previous value.
Defines constraints for the QgsCadUtils::alignMapPoint() method.
QgsCadUtils::AlignMapPointConstraint xyVertexConstraint
QgsCadUtils::AlignMapPointConstraint yConstraint
Constraint for Y coordinate.
QgsCadUtils::AlignMapPointConstraint xConstraint
Constraint for X coordinate.
double mapUnitsPerPixel
Map units/pixel ratio from map canvas.
void setCadPoints(const QList< QgsPoint > &points)
Sets the list of recent CAD points (in map coordinates).
void setLockedSnapVertices(const QQueue< QgsPointLocator::Match > &lockedSnapVertices)
Sets the queue of locked vertices.
QgsCadUtils::AlignMapPointConstraint mConstraint
Constraint for M coordinate.
QgsCadUtils::AlignMapPointConstraint distanceConstraint
Constraint for distance.
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.
Class for parsing and evaluation of expressions (formerly called "search strings").
A event filter for watching for focus events on a parent object.
void focusOut()
Emitted when parent object loses focus.
Map canvas is a class for displaying all GIS data types on a canvas.
QgsSnappingUtils * snappingUtils() const
Returns snapping utility class that is associated with map canvas.
void destinationCrsChanged()
Emitted when map CRS has changed.
QgsMapTool * mapTool()
Returns the currently active tool.
double mapUnitsPerPixel() const
Returns the mapUnitsPerPixel (map units per pixel) for the canvas.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QgsMapLayer * currentLayer()
returns current layer (set by legend widget)
Base class for all map layer types.
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
QgsPointXY originalMapPoint() const
Returns the original, unmodified map point of the mouse cursor.
void setMapPoint(const QgsPointXY &point)
Set the (snapped) point this event points to in map coordinates.
QgsPointXY snapPoint()
snapPoint will snap the points using the map canvas snapping utils configuration
QgsPointXY mapToLayerCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from output CRS to layer's CRS
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
bool isEditable() const override
Returns true if the layer can be edited.
A class to represent a 2D point.
Point geometry type, with support for z-dimension and m-values.
void setX(double x) SIP_HOLDGIL
Sets the point's x-coordinate.
void setY(double y) SIP_HOLDGIL
Sets the point's y-coordinate.
void setZ(double z) SIP_HOLDGIL
Sets the point's z-coordinate.
double distanceSquared(double x, double y) const SIP_HOLDGIL
Returns the Cartesian 2D squared distance between this point a specified x, y coordinate.
void setM(double m) SIP_HOLDGIL
Sets the point's m-value.
static QgsProject * instance()
Returns the QgsProject singleton instance.
void snappingConfigChanged(const QgsSnappingConfig &config)
Emitted whenever the configuration for snapping has changed.
QgsSnappingConfig snappingConfig
This class is a composition of two QSettings instances:
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Class that shows snapping marker on map canvas for the current snapping match.
This is a container for configuration of the snapping of the project.
void setTypeFlag(Qgis::SnappingTypes type)
define the type of snapping
void setMode(Qgis::SnappingMode mode)
define the mode of snapping
bool enabled() const
Returns if snapping is enabled.
This class has all the configuration of snapping and can return answers to snapping queries.
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.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Type
The WKB type describes the number of dimensions a geometry has.
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
@ PointCloudLayer
Point cloud layer. Added in QGIS 3.18.
@ MeshLayer
Mesh layer. Added in QGIS 3.2.
@ VectorLayer
Vector layer.
@ RasterLayer
Raster layer.
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ PluginLayer
Plugin based layer.
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...
QLineF segment(int index, QRectF rect, double radius)
QgsVectorLayer * layer() const
The vector layer where the snap occurred.
QgsPoint interpolatedPoint(const QgsCoordinateReferenceSystem &destinationCrs=QgsCoordinateReferenceSystem()) const
Convenient method to return a point on an edge with linear interpolation of the Z value.
QgsPointXY point() const
for vertex / edge match coords depending on what class returns it (geom.cache: layer coords,...
bool hasEdge() const
Returns true if the Match is an edge.
void edgePoints(QgsPointXY &pt1, QgsPointXY &pt2) const
Only for a valid edge match - obtain endpoints of the edge.
bool hasLineEndpoint() const
Returns true if the Match is a line endpoint (start or end vertex).
bool hasVertex() const
Returns true if the Match is a vertex.