18#include <QCoreApplication>
38#include <QActionGroup>
43 , mMapCanvas( canvas )
45 , mCommonAngleConstraint(
QgsSettings().value( QStringLiteral(
"/Cad/CommonAngle" ), 0.0 ).toDouble() )
51 mAngleConstraint.reset(
new CadConstraint( mAngleLineEdit, mLockAngleButton, mRelativeAngleButton, mRepeatingLockAngleButton ) );
52 mDistanceConstraint.reset(
new CadConstraint( mDistanceLineEdit, mLockDistanceButton,
nullptr, mRepeatingLockDistanceButton ) );
53 mXConstraint.reset(
new CadConstraint( mXLineEdit, mLockXButton, mRelativeXButton, mRepeatingLockXButton ) );
54 mYConstraint.reset(
new CadConstraint( mYLineEdit, mLockYButton, mRelativeYButton, mRepeatingLockYButton ) );
55 mZConstraint.reset(
new CadConstraint( mZLineEdit, mLockZButton, mRelativeZButton, mRepeatingLockZButton ) );
56 mMConstraint.reset(
new CadConstraint( mMLineEdit, mLockMButton, mRelativeMButton, mRepeatingLockMButton ) );
58 mLineExtensionConstraint.reset(
new CadConstraint(
new QLineEdit(),
new QToolButton() ) );
59 mXyVertexConstraint.reset(
new CadConstraint(
new QLineEdit(),
new QToolButton() ) );
63 mMapCanvas->installEventFilter(
this );
64 mAngleLineEdit->installEventFilter(
this );
65 mDistanceLineEdit->installEventFilter(
this );
66 mXLineEdit->installEventFilter(
this );
67 mYLineEdit->installEventFilter(
this );
68 mZLineEdit->installEventFilter(
this );
69 mMLineEdit->installEventFilter(
this );
72 connect( mEnableAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::activateCad );
73 connect( mConstructionModeAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::setConstructionMode );
74 connect( mParallelAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
75 connect( mPerpendicularAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
76 connect( mLockAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
77 connect( mLockDistanceButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
78 connect( mLockXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
79 connect( mLockYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
80 connect( mLockZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
81 connect( mLockMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
82 connect( mRelativeAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
83 connect( mRelativeXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
84 connect( mRelativeYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
85 connect( mRelativeZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
86 connect( mRelativeMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
87 connect( mRepeatingLockDistanceButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
88 connect( mRepeatingLockAngleButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
89 connect( mRepeatingLockXButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
90 connect( mRepeatingLockYButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
91 connect( mRepeatingLockZButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
92 connect( mRepeatingLockMButton, &QAbstractButton::clicked,
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
93 connect( mAngleLineEdit, &QLineEdit::returnPressed,
this, [ = ]() { lockConstraint(); } );
94 connect( mDistanceLineEdit, &QLineEdit::returnPressed,
this, [ = ]() { lockConstraint(); } );
95 connect( mXLineEdit, &QLineEdit::returnPressed,
this, [ = ]() { lockConstraint(); } );
96 connect( mYLineEdit, &QLineEdit::returnPressed,
this, [ = ]() { lockConstraint(); } );
97 connect( mZLineEdit, &QLineEdit::returnPressed,
this, [ = ]() { lockConstraint(); } );
98 connect( mMLineEdit, &QLineEdit::returnPressed,
this, [ = ]() { lockConstraint(); } );
99 connect( mAngleLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
100 connect( mDistanceLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
101 connect( mXLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
102 connect( mYLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
103 connect( mZLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
104 connect( mMLineEdit, &QLineEdit::textEdited,
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
120 QMenu *menu =
new QMenu(
this );
122 QActionGroup *angleButtonGroup =
new QActionGroup( menu );
123 mCommonAngleActions = QMap<QAction *, double>();
124 QList< QPair< double, QString > > commonAngles;
126 const QList<double> anglesDouble( { 0.0, 5.0, 10.0, 15.0, 18.0, 22.5, 30.0, 45.0, 90.0} );
127 for ( QList<double>::const_iterator it = anglesDouble.constBegin(); it != anglesDouble.constEnd(); ++it )
130 menuText = tr(
"Do Not Snap to Common Angles" );
132 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 );
133 commonAngles << QPair<double, QString>( *it, menuText );
135 for ( QList< QPair<double, QString > >::const_iterator it = commonAngles.constBegin(); it != commonAngles.constEnd(); ++it )
137 QAction *action =
new QAction( it->second, menu );
138 action->setCheckable(
true );
139 action->setChecked( it->first == mCommonAngleConstraint );
140 menu->addAction( action );
141 angleButtonGroup->addAction( action );
142 mCommonAngleActions.insert( action, it->first );
145 qobject_cast< QToolButton *>( mToolbar->widgetForAction( mSettingsAction ) )->setPopupMode( QToolButton::InstantPopup );
146 mSettingsAction->setMenu( menu );
147 mSettingsAction->setCheckable(
true );
148 mSettingsAction->setToolTip( tr(
"Snap to common angles" ) );
149 mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
150 connect( menu, &QMenu::triggered,
this, &QgsAdvancedDigitizingDockWidget::settingsButtonTriggered );
153 QMenu *constructionMenu =
new QMenu(
this );
155 mLineExtensionAction =
new QAction(
"Line Extension", constructionMenu );
156 mLineExtensionAction->setCheckable(
true );
157 constructionMenu->addAction( mLineExtensionAction );
158 connect( mLineExtensionAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
160 mXyVertexAction =
new QAction(
"X/Y Point", constructionMenu );
161 mXyVertexAction->setCheckable(
true );
162 constructionMenu->addAction( mXyVertexAction );
163 connect( mXyVertexAction, &QAction::triggered,
this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
165 auto constructionToolBar = qobject_cast< QToolButton *>( mToolbar->widgetForAction( mConstructionAction ) );
166 constructionToolBar->setPopupMode( QToolButton::InstantPopup );
167 constructionToolBar->setMenu( constructionMenu );
168 constructionToolBar->setObjectName( QStringLiteral(
"ConstructionButton" ) );
170 mConstructionAction->setMenu( menu );
171 mConstructionAction->setCheckable(
true );
172 mConstructionAction->setToolTip( tr(
"Construction Tools" ) );
176 mConstructionModeAction->setToolTip(
"<b>" + tr(
"Construction mode" ) +
"</b><br>(" + tr(
"press c to toggle on/off" ) +
")" );
177 mDistanceLineEdit->setToolTip(
"<b>" + tr(
"Distance" ) +
"</b><br>(" + tr(
"press d for quick access" ) +
")" );
178 mLockDistanceButton->setToolTip(
"<b>" + tr(
"Lock distance" ) +
"</b><br>(" + tr(
"press Ctrl + d for quick access" ) +
")" );
179 mRepeatingLockDistanceButton->setToolTip(
"<b>" + tr(
"Continuously lock distance" ) +
"</b>" );
181 mRelativeAngleButton->setToolTip(
"<b>" + tr(
"Toggles relative angle to previous segment" ) +
"</b><br>(" + tr(
"press Shift + a for quick access" ) +
")" );
182 mAngleLineEdit->setToolTip(
"<b>" + tr(
"Angle" ) +
"</b><br>(" + tr(
"press a for quick access" ) +
")" );
183 mLockAngleButton->setToolTip(
"<b>" + tr(
"Lock angle" ) +
"</b><br>(" + tr(
"press Ctrl + a for quick access" ) +
")" );
184 mRepeatingLockAngleButton->setToolTip(
"<b>" + tr(
"Continuously lock angle" ) +
"</b>" );
186 mRelativeXButton->setToolTip(
"<b>" + tr(
"Toggles relative x to previous node" ) +
"</b><br>(" + tr(
"press Shift + x for quick access" ) +
")" );
187 mXLineEdit->setToolTip(
"<b>" + tr(
"X coordinate" ) +
"</b><br>(" + tr(
"press x for quick access" ) +
")" );
188 mLockXButton->setToolTip(
"<b>" + tr(
"Lock x coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + x for quick access" ) +
")" );
189 mRepeatingLockXButton->setToolTip(
"<b>" + tr(
"Continuously lock x coordinate" ) +
"</b>" );
191 mRelativeYButton->setToolTip(
"<b>" + tr(
"Toggles relative y to previous node" ) +
"</b><br>(" + tr(
"press Shift + y for quick access" ) +
")" );
192 mYLineEdit->setToolTip(
"<b>" + tr(
"Y coordinate" ) +
"</b><br>(" + tr(
"press y for quick access" ) +
")" );
193 mLockYButton->setToolTip(
"<b>" + tr(
"Lock y coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + y for quick access" ) +
")" );
194 mRepeatingLockYButton->setToolTip(
"<b>" + tr(
"Continuously lock y coordinate" ) +
"</b>" );
196 mRelativeZButton->setToolTip(
"<b>" + tr(
"Toggles relative z to previous node" ) +
"</b><br>(" + tr(
"press Shift + z for quick access" ) +
")" );
197 mZLineEdit->setToolTip(
"<b>" + tr(
"Z coordinate" ) +
"</b><br>(" + tr(
"press z for quick access" ) +
")" );
198 mLockZButton->setToolTip(
"<b>" + tr(
"Lock z coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + z for quick access" ) +
")" );
199 mRepeatingLockZButton->setToolTip(
"<b>" + tr(
"Continuously lock z coordinate" ) +
"</b>" );
201 mRelativeMButton->setToolTip(
"<b>" + tr(
"Toggles relative m to previous node" ) +
"</b><br>(" + tr(
"press Shift + m for quick access" ) +
")" );
202 mMLineEdit->setToolTip(
"<b>" + tr(
"M coordinate" ) +
"</b><br>(" + tr(
"press m for quick access" ) +
")" );
203 mLockMButton->setToolTip(
"<b>" + tr(
"Lock m coordinate" ) +
"</b><br>(" + tr(
"press Ctrl + m for quick access" ) +
")" );
204 mRepeatingLockMButton->setToolTip(
"<b>" + tr(
"Continuously lock m coordinate" ) +
"</b>" );
217 mToggleFloaterAction->setChecked( mFloater->
active() );
219 updateCapacity(
true );
227 mXLineEdit->setText( value );
228 if ( mode == WidgetSetMode::ReturnPressed )
230 emit mXLineEdit->returnPressed();
232 else if ( mode == WidgetSetMode::FocusOut )
234 QEvent *e =
new QEvent( QEvent::FocusOut );
235 QCoreApplication::postEvent( mXLineEdit, e );
237 else if ( mode == WidgetSetMode::TextEdited )
239 emit mXLineEdit->textEdited( value );
244 mYLineEdit->setText( value );
245 if ( mode == WidgetSetMode::ReturnPressed )
247 emit mYLineEdit->returnPressed();
249 else if ( mode == WidgetSetMode::FocusOut )
251 QEvent *e =
new QEvent( QEvent::FocusOut );
252 QCoreApplication::postEvent( mYLineEdit, e );
254 else if ( mode == WidgetSetMode::TextEdited )
256 emit mYLineEdit->textEdited( value );
261 mZLineEdit->setText( value );
262 if ( mode == WidgetSetMode::ReturnPressed )
264 emit mZLineEdit->returnPressed();
266 else if ( mode == WidgetSetMode::FocusOut )
268 QEvent *e =
new QEvent( QEvent::FocusOut );
269 QCoreApplication::postEvent( mZLineEdit, e );
271 else if ( mode == WidgetSetMode::TextEdited )
273 emit mZLineEdit->textEdited( value );
278 mMLineEdit->setText( value );
279 if ( mode == WidgetSetMode::ReturnPressed )
281 emit mMLineEdit->returnPressed();
283 else if ( mode == WidgetSetMode::FocusOut )
285 QEvent *e =
new QEvent( QEvent::FocusOut );
286 QCoreApplication::postEvent( mMLineEdit, e );
288 else if ( mode == WidgetSetMode::TextEdited )
290 emit mMLineEdit->textEdited( value );
295 mAngleLineEdit->setText( value );
296 if ( mode == WidgetSetMode::ReturnPressed )
298 emit mAngleLineEdit->returnPressed();
300 else if ( mode == WidgetSetMode::FocusOut )
302 emit mAngleLineEdit->textEdited( value );
307 mDistanceLineEdit->setText( value );
308 if ( mode == WidgetSetMode::ReturnPressed )
310 emit mDistanceLineEdit->returnPressed();
312 else if ( mode == WidgetSetMode::FocusOut )
314 QEvent *e =
new QEvent( QEvent::FocusOut );
315 QCoreApplication::postEvent( mDistanceLineEdit, e );
317 else if ( mode == WidgetSetMode::TextEdited )
319 emit mDistanceLineEdit->textEdited( value );
324void QgsAdvancedDigitizingDockWidget::setCadEnabled(
bool enabled )
326 mCadEnabled = enabled;
327 mEnableAction->setChecked( enabled );
328 mConstructionModeAction->setEnabled( enabled );
329 mSettingsAction->setEnabled( enabled );
330 mInputWidgets->setEnabled( enabled );
331 mToggleFloaterAction->setEnabled( enabled );
332 mConstructionAction->setEnabled( enabled );
337 mLineExtensionAction->setChecked(
false );
338 mXyVertexAction->setChecked(
false );
340 mParallelAction->setEnabled(
false );
341 mPerpendicularAction->setEnabled(
false );
347 setConstructionMode(
false );
358 bool enableZ =
false;
359 bool enableM =
false;
363 switch ( layer->type() )
365 case Qgis::LayerType::Vector:
374 case Qgis::LayerType::Mesh:
376 QgsMeshLayer *mlayer = qobject_cast<QgsMeshLayer *>( layer );
381 case Qgis::LayerType::Raster:
382 case Qgis::LayerType::Plugin:
383 case Qgis::LayerType::VectorTile:
384 case Qgis::LayerType::Annotation:
385 case Qgis::LayerType::PointCloud:
386 case Qgis::LayerType::Group:
397 mRelativeZButton->setEnabled(
enable );
398 mZLabel->setEnabled(
enable );
399 mZLineEdit->setEnabled(
enable );
400 if ( mZLineEdit->isEnabled() )
404 mLockZButton->setEnabled(
enable );
410 mRelativeMButton->setEnabled(
enable );
411 mMLabel->setEnabled(
enable );
412 mMLineEdit->setEnabled(
enable );
413 if ( mMLineEdit->isEnabled() )
417 mLockMButton->setEnabled(
enable );
421void QgsAdvancedDigitizingDockWidget::activateCad(
bool enabled )
423 enabled &= mCurrentMapToolSupportsCad;
425 mSessionActive = enabled;
427 if ( enabled && !isVisible() )
432 setCadEnabled( enabled );
435void QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked(
bool activated )
441 else if ( sender() == mParallelAction )
445 else if ( sender() == mPerpendicularAction )
451void QgsAdvancedDigitizingDockWidget::setConstraintRelative(
bool activate )
453 if ( sender() == mRelativeAngleButton )
455 mAngleConstraint->setRelative( activate );
458 else if ( sender() == mRelativeXButton )
460 mXConstraint->setRelative( activate );
463 else if ( sender() == mRelativeYButton )
465 mYConstraint->setRelative( activate );
468 else if ( sender() == mRelativeZButton )
470 mZConstraint->setRelative( activate );
473 else if ( sender() == mRelativeMButton )
475 mMConstraint->setRelative( activate );
480void QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock(
bool activate )
482 if ( sender() == mRepeatingLockDistanceButton )
484 mDistanceConstraint->setRepeatingLock( activate );
486 else if ( sender() == mRepeatingLockAngleButton )
488 mAngleConstraint->setRepeatingLock( activate );
490 else if ( sender() == mRepeatingLockXButton )
492 mXConstraint->setRepeatingLock( activate );
494 else if ( sender() == mRepeatingLockYButton )
496 mYConstraint->setRepeatingLock( activate );
498 else if ( sender() == mRepeatingLockZButton )
500 mZConstraint->setRepeatingLock( activate );
502 else if ( sender() == mRepeatingLockMButton )
504 mMConstraint->setRepeatingLock( activate );
508void QgsAdvancedDigitizingDockWidget::setConstructionMode(
bool enabled )
510 mConstructionMode = enabled;
511 mConstructionModeAction->setChecked( enabled );
514void QgsAdvancedDigitizingDockWidget::settingsButtonTriggered( QAction *action )
517 const QMap<QAction *, double>::const_iterator ica = mCommonAngleActions.constFind( action );
518 if ( ica != mCommonAngleActions.constEnd() )
520 ica.key()->setChecked(
true );
521 mCommonAngleConstraint = ica.value();
523 mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
528QgsMapLayer *QgsAdvancedDigitizingDockWidget::targetLayer()
const
532 return advancedTool->layer();
546 if ( releaseRepeatingLocks )
548 mXyVertexAction->setChecked(
false );
552 mLineExtensionAction->setChecked(
false );
559 if ( releaseRepeatingLocks || !mAngleConstraint->isRepeatingLock() )
564 if ( releaseRepeatingLocks || !mDistanceConstraint->isRepeatingLock() )
569 if ( releaseRepeatingLocks || !mXConstraint->isRepeatingLock() )
574 if ( releaseRepeatingLocks || !mYConstraint->isRepeatingLock() )
579 if ( releaseRepeatingLocks || !mZConstraint->isRepeatingLock() )
584 if ( releaseRepeatingLocks || !mMConstraint->isRepeatingLock() )
590 if ( !mCadPointList.empty() )
592 if ( !mXConstraint->isLocked() && !mXConstraint->relative() )
594 mXConstraint->setValue( mCadPointList.constLast().x(),
true );
596 if ( !mYConstraint->isLocked() && !mYConstraint->relative() )
598 mYConstraint->setValue( mCadPointList.constLast().y(),
true );
600 if ( !mZConstraint->isLocked() && !mZConstraint->relative() )
602 mZConstraint->setValue( mCadPointList.constLast().z(),
true );
604 if ( !mMConstraint->isLocked() && !mMConstraint->relative() )
606 mMConstraint->setValue( mCadPointList.constLast().m(),
true );
613void QgsAdvancedDigitizingDockWidget::emit pointChanged()
616 QPoint globalPos = mMapCanvas->cursor().pos();
617 QPoint pos = mMapCanvas->mapFromGlobal( globalPos );
618 QMouseEvent *e =
new QMouseEvent( QEvent::MouseMove, pos, globalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier );
619 mCurrentMapTool->canvasMoveEvent( e );
625 CadConstraint *constraint =
nullptr;
626 if ( obj == mAngleLineEdit || obj == mLockAngleButton )
628 constraint = mAngleConstraint.get();
630 else if ( obj == mDistanceLineEdit || obj == mLockDistanceButton )
632 constraint = mDistanceConstraint.get();
634 else if ( obj == mXLineEdit || obj == mLockXButton )
636 constraint = mXConstraint.get();
638 else if ( obj == mYLineEdit || obj == mLockYButton )
640 constraint = mYConstraint.get();
642 else if ( obj == mZLineEdit || obj == mLockZButton )
644 constraint = mZConstraint.get();
646 else if ( obj == mMLineEdit || obj == mLockMButton )
648 constraint = mMConstraint.get();
650 else if ( obj == mLineExtensionAction )
652 constraint = mLineExtensionConstraint.get();
654 else if ( obj == mXyVertexAction )
656 constraint = mXyVertexConstraint.get();
661double QgsAdvancedDigitizingDockWidget::parseUserInput(
const QString &inputValue,
bool &ok )
const
673 const QVariant result = expr.evaluate();
674 if ( expr.hasEvalError() )
677 QString inputValueC { inputValue };
680 if ( inputValue.contains( QLocale().groupSeparator() ) )
682 inputValueC.remove( QLocale().groupSeparator() );
684 const QVariant resultC = exprC.evaluate();
685 if ( ! exprC.hasEvalError() )
687 value = resultC.toDouble( &ok );
692 if ( !ok && QLocale().decimalPoint() != QChar(
'.' ) && inputValueC.contains( QLocale().decimalPoint() ) )
694 QgsExpression exprC( inputValueC .replace( QLocale().decimalPoint(), QChar(
'.' ) ) );
695 const QVariant resultC = exprC.evaluate();
696 if ( ! exprC.hasEvalError() )
698 value = resultC.toDouble( &ok );
704 value = result.toDouble( &ok );
710void QgsAdvancedDigitizingDockWidget::updateConstraintValue( CadConstraint *constraint,
const QString &textValue,
bool convertExpression )
712 if ( !constraint || textValue.isEmpty() )
721 const double value = parseUserInput( textValue, ok );
725 constraint->setValue( value, convertExpression );
730void QgsAdvancedDigitizingDockWidget::lockConstraint(
bool activate )
732 CadConstraint *constraint = objectToConstraint( sender() );
740 const QString textValue = constraint->lineEdit()->text();
741 if ( !textValue.isEmpty() )
744 const double value = parseUserInput( textValue, ok );
747 constraint->setValue( value );
761 if ( constraint == mXConstraint.get() )
765 else if ( constraint == mYConstraint.get() )
769 else if ( constraint == mZConstraint.get() )
773 else if ( constraint == mMConstraint.get() )
777 else if ( constraint == mDistanceConstraint.get() )
781 else if ( constraint == mAngleConstraint.get() )
789 if ( constraint == mAngleConstraint.get() )
799void QgsAdvancedDigitizingDockWidget::constraintTextEdited(
const QString &textValue )
801 CadConstraint *constraint = objectToConstraint( sender() );
807 updateConstraintValue( constraint, textValue,
false );
810void QgsAdvancedDigitizingDockWidget::constraintFocusOut()
812 QLineEdit *lineEdit = qobject_cast< QLineEdit * >( sender()->parent() );
816 CadConstraint *constraint = objectToConstraint( lineEdit );
822 updateConstraintValue( constraint, lineEdit->text(),
true );
827 mBetweenLineConstraint = constraint;
832void QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint(
bool activate )
834 CadConstraint *constraint = objectToConstraint( sender() );
842 if ( constraint == mXyVertexConstraint.get() )
846 else if ( constraint == mLineExtensionConstraint.get() )
860void QgsAdvancedDigitizingDockWidget::updateCapacity(
bool updateUIwithoutChange )
862 CadCapacities newCapacities = CadCapacities();
866 if ( mCadPointList.count() > 1 )
875 if ( mCadPointList.count() > 2 )
880 if ( !updateUIwithoutChange && newCapacities == mCapacities )
890 const bool distance = mCadEnabled && newCapacities.testFlag(
Distance );
891 const bool relativeAngle = mCadEnabled && newCapacities.testFlag(
RelativeAngle );
892 const bool absoluteAngle = mCadEnabled && newCapacities.testFlag(
AbsoluteAngle );
893 const bool relativeCoordinates = mCadEnabled && newCapacities.testFlag(
RelativeCoordinates );
895 mPerpendicularAction->setEnabled( distance && snappingEnabled );
896 mParallelAction->setEnabled( distance && snappingEnabled );
898 mLineExtensionAction->setEnabled( snappingEnabled );
899 mXyVertexAction->setEnabled( snappingEnabled );
903 if ( !snappingEnabled )
905 mPerpendicularAction->setToolTip( tr(
"Snapping must be enabled to utilize perpendicular mode." ) );
906 mParallelAction->setToolTip( tr(
"Snapping must be enabled to utilize parallel mode." ) );
907 mLineExtensionAction->setToolTip( tr(
"Snapping must be enabled to utilize line extension mode." ) );
908 mXyVertexAction->setToolTip( tr(
"Snapping must be enabled to utilize xy point mode." ) );
910 else if ( mCadPointList.count() <= 1 )
912 mPerpendicularAction->setToolTip( tr(
"A first vertex should be drawn to utilize perpendicular mode." ) );
913 mParallelAction->setToolTip( tr(
"A first vertex should be drawn to utilize parallel mode." ) );
915 else if ( isGeographic )
917 mPerpendicularAction->setToolTip( tr(
"Perpendicular mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
918 mParallelAction->setToolTip( tr(
"Parallel mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
922 mPerpendicularAction->setToolTip(
"<b>" + tr(
"Perpendicular" ) +
"</b><br>(" + tr(
"press p to switch between perpendicular, parallel and normal mode" ) +
")" );
923 mParallelAction->setToolTip(
"<b>" + tr(
"Parallel" ) +
"</b><br>(" + tr(
"press p to switch between perpendicular, parallel and normal mode" ) +
")" );
927 if ( !absoluteAngle )
933 mLockAngleButton->setEnabled( absoluteAngle );
934 mRelativeAngleButton->setEnabled( relativeAngle );
935 mAngleLineEdit->setEnabled( absoluteAngle );
937 if ( !absoluteAngle )
941 if ( !relativeAngle )
943 mAngleConstraint->setRelative(
false );
946 else if ( relativeAngle && !mCapacities.testFlag(
RelativeAngle ) )
949 mAngleConstraint->setRelative(
true );
954 mLockDistanceButton->setEnabled( distance && relativeCoordinates );
955 mDistanceLineEdit->setEnabled( distance && relativeCoordinates );
957 if ( !( distance && relativeCoordinates ) )
962 mRelativeXButton->setEnabled( relativeCoordinates );
963 mRelativeYButton->setEnabled( relativeCoordinates );
964 mRelativeZButton->setEnabled( relativeCoordinates );
965 mRelativeMButton->setEnabled( relativeCoordinates );
968 mCapacities = newCapacities;
978 constr.
value =
c->value();
985 if ( !mLineExtensionConstraint->isLocked() && !mXyVertexConstraint->isLocked() )
991 const int lastIndex = mLockedSnapVertices.length() - 1;
992 for (
int i = lastIndex ; i >= 0; --i )
994 if ( mLockedSnapVertices[i].point() == snapMatch.
point() )
996 if ( snapMatch.
point() != previouslySnap.
point() )
998 mLockedSnapVertices.removeAt( i );
1004 if ( snapMatch.
point() != previouslySnap.
point() )
1006 mLockedSnapVertices.enqueue( snapMatch );
1009 if ( mLockedSnapVertices.count() > 3 )
1011 mLockedSnapVertices.dequeue();
1020 context.
xConstraint = _constraint( mXConstraint.get() );
1021 context.
yConstraint = _constraint( mYConstraint.get() );
1022 context.
zConstraint = _constraint( mZConstraint.get() );
1023 context.
mConstraint = _constraint( mMConstraint.get() );
1039 const bool res = output.
valid;
1041 mSnappedSegment.clear();
1046 mSnappedSegment << edgePt0 << edgePt1;
1067 mSnapIndicator->setMatch( output.
snapMatch );
1068 mSnapIndicator->setVisible(
true );
1072 mSnapIndicator->setVisible(
false );
1093 if ( mSnapMatch.
layer() )
1105 toggleLockedSnapVertex( mSnapMatch, mLastSnapMatch );
1106 mLastSnapMatch = mSnapMatch;
1116 if ( mLockZButton->isChecked() )
1118 point.
setZ( QLocale().toDouble( mZLineEdit->text() ) );
1120 if ( mLockMButton->isChecked() )
1122 point.
setM( QLocale().toDouble( mMLineEdit->text() ) );
1126 updateCurrentPoint( point );
1128 updateUnlockedConstraintValues( point );
1136 emit
pushWarning( tr(
"Some constraints are incompatible. Resulting point might be incorrect." ) );
1143void QgsAdvancedDigitizingDockWidget::updateUnlockedConstraintValues(
const QgsPoint &point )
1145 bool previousPointExist, penulPointExist;
1150 if ( !mAngleConstraint->isLocked() && previousPointExist )
1153 if ( penulPointExist && mAngleConstraint->relative() )
1156 angle = std::atan2( previousPt.
y() - penultimatePt.
y(),
1157 previousPt.
x() - penultimatePt.
x() );
1159 angle = ( std::atan2( point.
y() - previousPt.
y(),
1160 point.
x() - previousPt.
x()
1161 ) -
angle ) * 180 / M_PI;
1164 mAngleConstraint->setValue(
angle );
1167 if ( !mDistanceConstraint->isLocked() && previousPointExist )
1169 mDistanceConstraint->setValue( std::sqrt( previousPt.
distanceSquared( point ) ) );
1172 if ( !mXConstraint->isLocked() )
1174 if ( previousPointExist && mXConstraint->relative() )
1176 mXConstraint->setValue( point.
x() - previousPt.
x() );
1180 mXConstraint->setValue( point.
x() );
1184 if ( !mYConstraint->isLocked() )
1186 if ( previousPointExist && mYConstraint->relative() )
1188 mYConstraint->setValue( point.
y() - previousPt.
y() );
1192 mYConstraint->setValue( point.
y() );
1196 if ( !mZConstraint->isLocked() )
1198 if ( previousPointExist && mZConstraint->relative() )
1200 mZConstraint->setValue( point.
z() - previousPt.
z() );
1204 mZConstraint->setValue( point.
z() );
1208 if ( !mMConstraint->isLocked() )
1210 if ( previousPointExist && mMConstraint->relative() )
1212 mMConstraint->setValue( point.
m() - previousPt.
m() );
1216 mMConstraint->setValue( point.
m() );
1222QList<QgsPointXY> QgsAdvancedDigitizingDockWidget::snapSegmentToAllLayers(
const QgsPointXY &originalMapPoint,
bool *snapped )
const
1234 localConfig.
setTypeFlag( Qgis::SnappingType::Segment );
1235 snappingUtils->
setConfig( localConfig );
1237 match = snappingUtils->
snapToMap( originalMapPoint,
nullptr,
true );
1239 snappingUtils->
setConfig( canvasConfig );
1249 *snapped =
segment.count() == 2;
1262 bool previousPointExist, penulPointExist, snappedSegmentExist;
1265 mSnappedSegment = snapSegmentToAllLayers( e->
originalMapPoint(), &snappedSegmentExist );
1267 if ( !previousPointExist || !snappedSegmentExist )
1272 double angle = std::atan2( mSnappedSegment[0].y() - mSnappedSegment[1].y(), mSnappedSegment[0].x() - mSnappedSegment[1].x() );
1274 if ( mAngleConstraint->relative() && penulPointExist )
1276 angle -= std::atan2( previousPt.
y() - penultimatePt.
y(), previousPt.
x() - penultimatePt.
x() );
1284 angle *= 180 / M_PI;
1286 mAngleConstraint->setValue(
angle );
1287 mAngleConstraint->setLockMode( lockMode );
1305 case Qt::Key_Backspace:
1306 case Qt::Key_Delete:
1312 case Qt::Key_Escape:
1342 case Qt::Key_Backspace:
1343 case Qt::Key_Delete:
1349 case Qt::Key_Escape:
1356 filterKeyPress( e );
1365 const auto constPoints = points;
1372bool QgsAdvancedDigitizingDockWidget::eventFilter( QObject *obj, QEvent *event )
1376 return QgsDockWidget::eventFilter( obj, event );
1384 if ( event->type() == QEvent::ShortcutOverride || event->type() == QEvent::KeyPress )
1386 if ( QKeyEvent *keyEvent =
dynamic_cast<QKeyEvent *
>( event ) )
1388 return filterKeyPress( keyEvent );
1391 return QgsDockWidget::eventFilter( obj, event );
1394bool QgsAdvancedDigitizingDockWidget::filterKeyPress( QKeyEvent *e )
1400 const QEvent::Type type = e->type();
1406 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1408 mXConstraint->toggleLocked();
1413 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1417 mXConstraint->toggleRelative();
1424 else if ( type == QEvent::KeyPress )
1426 mXLineEdit->setFocus();
1427 mXLineEdit->selectAll();
1436 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1438 mYConstraint->toggleLocked();
1443 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1447 mYConstraint->toggleRelative();
1454 else if ( type == QEvent::KeyPress )
1456 mYLineEdit->setFocus();
1457 mYLineEdit->selectAll();
1466 if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::AltModifier )
1468 mZConstraint->toggleLocked();
1473 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1477 mZConstraint->toggleRelative();
1484 else if ( type == QEvent::KeyPress )
1486 mZLineEdit->setFocus();
1487 mZLineEdit->selectAll();
1496 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1498 mMConstraint->toggleLocked();
1503 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1507 mMConstraint->toggleRelative();
1514 else if ( type == QEvent::KeyPress )
1516 mMLineEdit->setFocus();
1517 mMLineEdit->selectAll();
1526 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1530 mAngleConstraint->toggleLocked();
1536 else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1540 mAngleConstraint->toggleRelative();
1547 else if ( type == QEvent::KeyPress )
1549 mAngleLineEdit->setFocus();
1550 mAngleLineEdit->selectAll();
1559 if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1563 mDistanceConstraint->toggleLocked();
1570 else if ( type == QEvent::KeyPress )
1572 mDistanceLineEdit->setFocus();
1573 mDistanceLineEdit->selectAll();
1581 if ( type == QEvent::KeyPress )
1583 setConstructionMode( !mConstructionMode );
1590 if ( type == QEvent::KeyPress )
1592 const bool parallel = mParallelAction->isChecked();
1593 const bool perpendicular = mPerpendicularAction->isChecked();
1595 if ( !parallel && !perpendicular )
1599 else if ( perpendicular )
1619 return e->isAccepted();
1628 mAngleLineEdit->setToolTip( tr(
"Angle constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1629 mDistanceLineEdit->setToolTip( tr(
"Distance constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1631 mLabelX->setText( tr(
"Long" ) );
1632 mLabelY->setText( tr(
"Lat" ) );
1634 mXConstraint->setPrecision( 8 );
1635 mYConstraint->setPrecision( 8 );
1639 mAngleLineEdit->setToolTip(
"<b>" + tr(
"Angle" ) +
"</b><br>(" + tr(
"press a for quick access" ) +
")" );
1640 mAngleLineEdit->setToolTip( QString() );
1642 mDistanceLineEdit->setToolTip(
"<b>" + tr(
"Distance" ) +
"</b><br>(" + tr(
"press d for quick access" ) +
")" );
1644 mLabelX->setText( tr(
"x" ) );
1645 mLabelY->setText( tr(
"y" ) );
1647 mXConstraint->setPrecision( 6 );
1648 mYConstraint->setPrecision( 6 );
1653 mEnableAction->setEnabled(
true );
1654 mErrorLabel->hide();
1657 mCurrentMapToolSupportsCad =
true;
1659 if ( mSessionActive && !isVisible() )
1663 setCadEnabled( mSessionActive );
1670 mEnableAction->setEnabled(
false );
1671 mErrorLabel->setText( tr(
"CAD tools are not enabled for the current map tool" ) );
1672 mErrorLabel->show();
1675 mCurrentMapToolSupportsCad =
false;
1677 mSnapIndicator->setVisible(
false );
1679 setCadEnabled(
false );
1684 mCadPaintItem->update();
1689 if ( !force && ( mLineExtensionConstraint->isLocked() || mXyVertexConstraint->isLocked() ) )
1694 mLockedSnapVertices.clear();
1700 QgsPoint pt = pointXYToPoint( point );
1703 mCadPointList << pt;
1707 mCadPointList.insert( 0, pt );
1720 mCadPointList.removeAt( i );
1727 mCadPointList.clear();
1728 mSnappedSegment.clear();
1734void QgsAdvancedDigitizingDockWidget::updateCurrentPoint(
const QgsPoint &point )
1738 mCadPointList << point;
1743 mCadPointList[0] = point;
1752 mLockerButton->setChecked( mode ==
HardLock );
1753 if ( mRepeatingLockButton )
1757 mRepeatingLockButton->setEnabled(
true );
1761 mRepeatingLockButton->setChecked(
false );
1762 mRepeatingLockButton->setEnabled(
false );
1775 mRepeatingLock = repeating;
1776 if ( mRepeatingLockButton )
1777 mRepeatingLockButton->setChecked( repeating );
1782 mRelative = relative;
1783 if ( mRelativeButton )
1785 mRelativeButton->setChecked( relative );
1792 if ( updateWidget && mLineEdit->isEnabled() )
1793 mLineEdit->setText( QLocale().toString( value,
'f', mPrecision ) );
1798 setLockMode( mLockMode == HardLock ? NoLock : HardLock );
1803 setRelative( !mRelative );
1809 if ( mLineEdit->isEnabled() )
1810 mLineEdit->setText( QLocale().toString( mValue,
'f', mPrecision ) );
1818 return mCadPointList.value( 0 );
1827 QgsPoint res = mCadPointList.value( 0 );
1829 res.
setX( layerCoordinates.
x() );
1830 res.
setY( layerCoordinates.
y() );
1841 return mCadPointList.value( 1 );
1851 return mCadPointList.value( 2 );
1856QgsPoint QgsAdvancedDigitizingDockWidget::pointXYToPoint(
const QgsPointXY &point )
const
1863 return mZLineEdit->isEnabled() ? QLocale().toDouble( mZLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
1868 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.
WkbType
The WKB type describes the number of dimensions a geometry has.
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 Qgis::WkbType wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
static bool hasZ(Qgis::WkbType type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
static bool hasM(Qgis::WkbType type) SIP_HOLDGIL
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...
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.