18#include <QCoreApplication> 
   23#include "moc_qgsadvanceddigitizingdockwidget.cpp" 
   46#include <QActionGroup> 
   57  , mMapCanvas( canvas )
 
   58  , mUserInputWidget( userInputWidget )
 
   60  , mCommonAngleConstraint( 
QgsSettings().value( QStringLiteral( 
"/Cad/CommonAngle" ), 0.0 ).toDouble() )
 
   66  mAngleConstraint.reset( 
new CadConstraint( mAngleLineEdit, mLockAngleButton, mRelativeAngleButton, mRepeatingLockAngleButton ) );
 
   68  mAngleConstraint->setMapCanvas( mMapCanvas );
 
   69  mDistanceConstraint.reset( 
new CadConstraint( mDistanceLineEdit, mLockDistanceButton, 
nullptr, mRepeatingLockDistanceButton ) );
 
   71  mDistanceConstraint->setMapCanvas( mMapCanvas );
 
   72  mXConstraint.reset( 
new CadConstraint( mXLineEdit, mLockXButton, mRelativeXButton, mRepeatingLockXButton ) );
 
   74  mXConstraint->setMapCanvas( mMapCanvas );
 
   75  mYConstraint.reset( 
new CadConstraint( mYLineEdit, mLockYButton, mRelativeYButton, mRepeatingLockYButton ) );
 
   77  mYConstraint->setMapCanvas( mMapCanvas );
 
   78  mZConstraint.reset( 
new CadConstraint( mZLineEdit, mLockZButton, mRelativeZButton, mRepeatingLockZButton ) );
 
   80  mZConstraint->setMapCanvas( mMapCanvas );
 
   81  mMConstraint.reset( 
new CadConstraint( mMLineEdit, mLockMButton, mRelativeMButton, mRepeatingLockMButton ) );
 
   83  mMConstraint->setMapCanvas( mMapCanvas );
 
   85  mLineExtensionConstraint.reset( 
new CadConstraint( 
new QLineEdit(), 
new QToolButton() ) );
 
   86  mXyVertexConstraint.reset( 
new CadConstraint( 
new QLineEdit(), 
new QToolButton() ) );
 
   87  mXyVertexConstraint->setMapCanvas( mMapCanvas );
 
   91  mMapCanvas->installEventFilter( 
this );
 
   92  mAngleLineEdit->installEventFilter( 
this );
 
   93  mDistanceLineEdit->installEventFilter( 
this );
 
   94  mXLineEdit->installEventFilter( 
this );
 
   95  mYLineEdit->installEventFilter( 
this );
 
   96  mZLineEdit->installEventFilter( 
this );
 
   97  mMLineEdit->installEventFilter( 
this );
 
  100  connect( mEnableAction, &QAction::triggered, 
this, &QgsAdvancedDigitizingDockWidget::activateCad );
 
  101  connect( mConstructionModeAction, &QAction::triggered, 
this, &QgsAdvancedDigitizingDockWidget::setConstructionMode );
 
  102  connect( mParallelAction, &QAction::triggered, 
this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
 
  103  connect( mPerpendicularAction, &QAction::triggered, 
this, &QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked );
 
  104  connect( mLockAngleButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
 
  105  connect( mLockDistanceButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
 
  106  connect( mLockXButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
 
  107  connect( mLockYButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
 
  108  connect( mLockZButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
 
  109  connect( mLockMButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
 
  110  connect( mRelativeAngleButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
 
  111  connect( mRelativeXButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
 
  112  connect( mRelativeYButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
 
  113  connect( mRelativeZButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
 
  114  connect( mRelativeMButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
 
  115  connect( mRepeatingLockDistanceButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
 
  116  connect( mRepeatingLockAngleButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
 
  117  connect( mRepeatingLockXButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
 
  118  connect( mRepeatingLockYButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
 
  119  connect( mRepeatingLockZButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
 
  120  connect( mRepeatingLockMButton, &QAbstractButton::clicked, 
this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
 
  121  connect( mAngleLineEdit, &QLineEdit::returnPressed, 
this, [=]() { lockConstraint(); } );
 
  122  connect( mDistanceLineEdit, &QLineEdit::returnPressed, 
this, [=]() { lockConstraint(); } );
 
  123  connect( mXLineEdit, &QLineEdit::returnPressed, 
this, [=]() { lockConstraint(); } );
 
  124  connect( mYLineEdit, &QLineEdit::returnPressed, 
this, [=]() { lockConstraint(); } );
 
  125  connect( mZLineEdit, &QLineEdit::returnPressed, 
this, [=]() { lockConstraint(); } );
 
  126  connect( mMLineEdit, &QLineEdit::returnPressed, 
this, [=]() { lockConstraint(); } );
 
  127  connect( mAngleLineEdit, &QLineEdit::textEdited, 
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
 
  128  connect( mDistanceLineEdit, &QLineEdit::textEdited, 
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
 
  129  connect( mXLineEdit, &QLineEdit::textEdited, 
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
 
  130  connect( mYLineEdit, &QLineEdit::textEdited, 
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
 
  131  connect( mZLineEdit, &QLineEdit::textEdited, 
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
 
  132  connect( mMLineEdit, &QLineEdit::textEdited, 
this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
 
  138    whileBlocking( mAngleLineEdit )->setText( cleanedInputValue );
 
  144    whileBlocking( mDistanceLineEdit )->setText( cleanedInputValue );
 
  156  mCommonAngleActionsMenu = 
new QMenu( 
this );
 
  158#ifndef __clang_analyzer__ 
  159  QActionGroup *angleButtonGroup = 
new QActionGroup( mCommonAngleActionsMenu ); 
 
  161  QList<QPair<double, QString>> commonAngles;
 
  162  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 } );
 
  163  for ( QList<double>::const_iterator it = anglesDouble.constBegin(); it != anglesDouble.constEnd(); ++it )
 
  169    QMenu *snappingPriorityMenu = 
new QMenu( tr( 
"Snapping Priority" ), mCommonAngleActionsMenu );
 
  170    QActionGroup *snappingPriorityActionGroup = 
new QActionGroup( snappingPriorityMenu );
 
  171    QAction *featuresAction = 
new QAction( tr( 
"Prioritize Snapping to Features" ), snappingPriorityActionGroup );
 
  172    featuresAction->setCheckable( 
true );
 
  173    QAction *anglesAction = 
new QAction( tr( 
"Prioritize Snapping to Common Angles" ), snappingPriorityActionGroup );
 
  174    anglesAction->setCheckable( 
true );
 
  175    snappingPriorityActionGroup->addAction( featuresAction );
 
  176    snappingPriorityActionGroup->addAction( anglesAction );
 
  177    snappingPriorityMenu->addAction( anglesAction );
 
  178    snappingPriorityMenu->addAction( featuresAction );
 
  179    connect( anglesAction, &QAction::changed, 
this, [=] {
 
  180      mSnappingPrioritizeFeatures = featuresAction->isChecked();
 
  181      settingsCadSnappingPriorityPrioritizeFeature->
setValue( featuresAction->isChecked() );
 
  183    featuresAction->setChecked( settingsCadSnappingPriorityPrioritizeFeature->
value() );
 
  184    anglesAction->setChecked( !featuresAction->isChecked() );
 
  185    mCommonAngleActionsMenu->addMenu( snappingPriorityMenu );
 
  188  for ( QList<QPair<double, QString>>::const_iterator it = commonAngles.constBegin(); it != commonAngles.constEnd(); ++it )
 
  190    QAction *action = 
new QAction( it->second, mCommonAngleActionsMenu );
 
  191    action->setCheckable( 
true );
 
  192    action->setChecked( it->first == mCommonAngleConstraint );
 
  193    mCommonAngleActionsMenu->addAction( action );
 
  195#ifndef __clang_analyzer__ 
  196    angleButtonGroup->addAction( action );
 
  198    mCommonAngleActions.insert( it->first, action );
 
  202  QMenu *constructionSettingsMenu = 
new QMenu( 
this );
 
  204  mRecordConstructionGuides = 
new QAction( tr( 
"Record Construction Guides" ), constructionSettingsMenu );
 
  205  mRecordConstructionGuides->setCheckable( 
true );
 
  206  mRecordConstructionGuides->setChecked( settingsCadRecordConstructionGuides->
value() );
 
  207  constructionSettingsMenu->addAction( mRecordConstructionGuides );
 
  208  connect( mRecordConstructionGuides, &QAction::triggered, 
this, [=]() { settingsCadRecordConstructionGuides->
setValue( mRecordConstructionGuides->isChecked() ); } );
 
  210  mShowConstructionGuides = 
new QAction( tr( 
"Show Construction Guides" ), constructionSettingsMenu );
 
  211  mShowConstructionGuides->setCheckable( 
true );
 
  212  mShowConstructionGuides->setChecked( settingsCadShowConstructionGuides->
value() );
 
  213  constructionSettingsMenu->addAction( mShowConstructionGuides );
 
  214  connect( mShowConstructionGuides, &QAction::triggered, 
this, [=]() {
 
  215    settingsCadShowConstructionGuides->
setValue( mShowConstructionGuides->isChecked() );
 
  219  mSnapToConstructionGuides = 
new QAction( tr( 
"Snap to Visible Construction Guides" ), constructionSettingsMenu );
 
  220  mSnapToConstructionGuides->setCheckable( 
true );
 
  221  mSnapToConstructionGuides->setChecked( settingsCadSnapToConstructionGuides->
value() );
 
  222  constructionSettingsMenu->addAction( mSnapToConstructionGuides );
 
  223  connect( mSnapToConstructionGuides, &QAction::triggered, 
this, [=]() { settingsCadSnapToConstructionGuides->
setValue( mSnapToConstructionGuides->isChecked() ); } );
 
  225  constructionSettingsMenu->addSeparator();
 
  227  mClearConstructionGuides = 
new QAction( tr( 
"Clear Construction Guides" ), constructionSettingsMenu );
 
  228  constructionSettingsMenu->addAction( mClearConstructionGuides );
 
  229  connect( mClearConstructionGuides, &QAction::triggered, 
this, [=]() {
 
  230    resetConstructionGuides();
 
  234  QToolButton *constructionModeToolButton = qobject_cast<QToolButton *>( mToolbar->widgetForAction( mConstructionModeAction ) );
 
  235  constructionModeToolButton->setPopupMode( QToolButton::MenuButtonPopup );
 
  236  constructionModeToolButton->setMenu( constructionSettingsMenu );
 
  237  constructionModeToolButton->setObjectName( QStringLiteral( 
"ConstructionModeButton" ) );
 
  240  QMenu *toolsMenu = 
new QMenu( 
this );
 
  241  connect( toolsMenu, &QMenu::aboutToShow, 
this, [=]() {
 
  244    for ( 
const QString &name : toolMetadataNames )
 
  247      QAction *toolAction = 
new QAction( toolMetadata->
icon(), toolMetadata->
visibleName(), toolsMenu );
 
  248      connect( toolAction, &QAction::triggered, 
this, [=]() {
 
  251      toolsMenu->addAction( toolAction );
 
  254  qobject_cast<QToolButton *>( mToolbar->widgetForAction( mToolsAction ) )->setPopupMode( QToolButton::InstantPopup );
 
  255  mToolsAction->setMenu( toolsMenu );
 
  257  qobject_cast<QToolButton *>( mToolbar->widgetForAction( mSettingsAction ) )->setPopupMode( QToolButton::InstantPopup );
 
  258  mSettingsAction->setMenu( mCommonAngleActionsMenu );
 
  259  mSettingsAction->setCheckable( 
true );
 
  260  mSettingsAction->setToolTip( 
"<b>" + tr( 
"Snap to common angles" ) + 
"</b><br>(" + tr( 
"press n to cycle through the options" ) + 
")" );
 
  261  mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
 
  262  connect( mCommonAngleActionsMenu, &QMenu::triggered, 
this, &QgsAdvancedDigitizingDockWidget::settingsButtonTriggered );
 
  265  QMenu *constructionMenu = 
new QMenu( 
this );
 
  267  mLineExtensionAction = 
new QAction( tr( 
"Line Extension" ), constructionMenu );
 
  268  mLineExtensionAction->setCheckable( 
true );
 
  269  constructionMenu->addAction( mLineExtensionAction );
 
  270  connect( mLineExtensionAction, &QAction::triggered, 
this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
 
  272  mXyVertexAction = 
new QAction( tr( 
"X/Y Point" ), constructionMenu );
 
  273  mXyVertexAction->setCheckable( 
true );
 
  274  constructionMenu->addAction( mXyVertexAction );
 
  275  connect( mXyVertexAction, &QAction::triggered, 
this, &QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint );
 
  277  auto constructionToolBar = qobject_cast<QToolButton *>( mToolbar->widgetForAction( mConstructionAction ) );
 
  278  constructionToolBar->setPopupMode( QToolButton::InstantPopup );
 
  279  constructionToolBar->setMenu( constructionMenu );
 
  280  constructionToolBar->setObjectName( QStringLiteral( 
"ConstructionButton" ) );
 
  282  mConstructionAction->setMenu( mCommonAngleActionsMenu );
 
  283  mConstructionAction->setCheckable( 
true );
 
  284  mConstructionAction->setToolTip( tr( 
"Construction Tools" ) );
 
  288  mConstructionModeAction->setToolTip( 
"<b>" + tr( 
"Construction mode" ) + 
"</b><br>(" + tr( 
"press c to toggle on/off" ) + 
")" );
 
  289  mDistanceLineEdit->setToolTip( 
"<b>" + tr( 
"Distance" ) + 
"</b><br>(" + tr( 
"press d for quick access" ) + 
")" );
 
  290  mLockDistanceButton->setToolTip( 
"<b>" + tr( 
"Lock distance" ) + 
"</b><br>(" + tr( 
"press Ctrl + d for quick access" ) + 
")" );
 
  291  mRepeatingLockDistanceButton->setToolTip( 
"<b>" + tr( 
"Continuously lock distance" ) + 
"</b>" );
 
  293  mRelativeAngleButton->setToolTip( 
"<b>" + tr( 
"Toggles relative angle to previous segment" ) + 
"</b><br>(" + tr( 
"press Shift + a for quick access" ) + 
")" );
 
  294  mAngleLineEdit->setToolTip( 
"<b>" + tr( 
"Angle" ) + 
"</b><br>(" + tr( 
"press a for quick access" ) + 
")" );
 
  295  mLockAngleButton->setToolTip( 
"<b>" + tr( 
"Lock angle" ) + 
"</b><br>(" + tr( 
"press Ctrl + a for quick access" ) + 
")" );
 
  296  mRepeatingLockAngleButton->setToolTip( 
"<b>" + tr( 
"Continuously lock angle" ) + 
"</b>" );
 
  298  mRelativeXButton->setToolTip( 
"<b>" + tr( 
"Toggles relative x to previous node" ) + 
"</b><br>(" + tr( 
"press Shift + x for quick access" ) + 
")" );
 
  299  mXLineEdit->setToolTip( 
"<b>" + tr( 
"X coordinate" ) + 
"</b><br>(" + tr( 
"press x for quick access" ) + 
")" );
 
  300  mLockXButton->setToolTip( 
"<b>" + tr( 
"Lock x coordinate" ) + 
"</b><br>(" + tr( 
"press Ctrl + x for quick access" ) + 
")" );
 
  301  mRepeatingLockXButton->setToolTip( 
"<b>" + tr( 
"Continuously lock x coordinate" ) + 
"</b>" );
 
  303  mRelativeYButton->setToolTip( 
"<b>" + tr( 
"Toggles relative y to previous node" ) + 
"</b><br>(" + tr( 
"press Shift + y for quick access" ) + 
")" );
 
  304  mYLineEdit->setToolTip( 
"<b>" + tr( 
"Y coordinate" ) + 
"</b><br>(" + tr( 
"press y for quick access" ) + 
")" );
 
  305  mLockYButton->setToolTip( 
"<b>" + tr( 
"Lock y coordinate" ) + 
"</b><br>(" + tr( 
"press Ctrl + y for quick access" ) + 
")" );
 
  306  mRepeatingLockYButton->setToolTip( 
"<b>" + tr( 
"Continuously lock y coordinate" ) + 
"</b>" );
 
  308  mRelativeZButton->setToolTip( 
"<b>" + tr( 
"Toggles relative z to previous node" ) + 
"</b><br>(" + tr( 
"press Shift + z for quick access" ) + 
")" );
 
  309  mZLineEdit->setToolTip( 
"<b>" + tr( 
"Z coordinate" ) + 
"</b><br>(" + tr( 
"press z for quick access" ) + 
")" );
 
  310  mLockZButton->setToolTip( 
"<b>" + tr( 
"Lock z coordinate" ) + 
"</b><br>(" + tr( 
"press Ctrl + z for quick access" ) + 
")" );
 
  311  mRepeatingLockZButton->setToolTip( 
"<b>" + tr( 
"Continuously lock z coordinate" ) + 
"</b>" );
 
  313  mRelativeMButton->setToolTip( 
"<b>" + tr( 
"Toggles relative m to previous node" ) + 
"</b><br>(" + tr( 
"press Shift + m for quick access" ) + 
")" );
 
  314  mMLineEdit->setToolTip( 
"<b>" + tr( 
"M coordinate" ) + 
"</b><br>(" + tr( 
"press m for quick access" ) + 
")" );
 
  315  mLockMButton->setToolTip( 
"<b>" + tr( 
"Lock m coordinate" ) + 
"</b><br>(" + tr( 
"press Ctrl + m for quick access" ) + 
")" );
 
  316  mRepeatingLockMButton->setToolTip( 
"<b>" + tr( 
"Continuously lock m coordinate" ) + 
"</b>" );
 
  327  mFloaterActionsMenu = 
new QMenu( 
this );
 
  328  qobject_cast<QToolButton *>( mToolbar->widgetForAction( mFloaterAction ) )->setPopupMode( QToolButton::InstantPopup );
 
  329  mFloaterAction->setMenu( mFloaterActionsMenu );
 
  330  mFloaterAction->setCheckable( 
true );
 
  332  mFloaterAction->setChecked( mFloater->
active() );
 
  336    QAction *action = 
new QAction( tr( 
"Show floater" ), mFloaterActionsMenu );
 
  337    action->setCheckable( 
true );
 
  338    action->setChecked( mFloater->
active() );
 
  339    mFloaterActionsMenu->addAction( action );
 
  340    connect( action, &QAction::toggled, 
this, [=]( 
bool checked ) {
 
  342      mFloaterAction->setChecked( checked );
 
  346  mFloaterActionsMenu->addSeparator();
 
  349    QAction *action = 
new QAction( tr( 
"Show distance" ), mFloaterActionsMenu );
 
  350    action->setCheckable( 
true );
 
  351    mFloaterActionsMenu->addAction( action );
 
  352    connect( action, &QAction::toggled, 
this, [=]( 
bool checked ) {
 
  355    action->setChecked( 
QgsSettings().value( QStringLiteral( 
"/Cad/DistanceShowInFloater" ), 
true ).toBool() );
 
  359    QAction *action = 
new QAction( tr( 
"Show angle" ), mFloaterActionsMenu );
 
  360    action->setCheckable( 
true );
 
  361    mFloaterActionsMenu->addAction( action );
 
  362    connect( action, &QAction::toggled, 
this, [=]( 
bool checked ) {
 
  365    action->setChecked( 
QgsSettings().value( QStringLiteral( 
"/Cad/AngleShowInFloater" ), 
true ).toBool() );
 
  369    QAction *action = 
new QAction( tr( 
"Show XY coordinates" ), mFloaterActionsMenu );
 
  370    action->setCheckable( 
true );
 
  371    mFloaterActionsMenu->addAction( action );
 
  372    connect( action, &QAction::toggled, 
this, [=]( 
bool checked ) {
 
  377    action->setChecked( 
QgsSettings().value( QStringLiteral( 
"/Cad/XCoordinateShowInFloater" ), 
true ).toBool() );
 
  381    QAction *action = 
new QAction( tr( 
"Show Z value" ), mFloaterActionsMenu );
 
  382    action->setCheckable( 
true );
 
  383    mFloaterActionsMenu->addAction( action );
 
  384    connect( action, &QAction::toggled, 
this, [=]( 
bool checked ) {
 
  387    action->setChecked( 
QgsSettings().value( QStringLiteral( 
"/Cad/ZCoordinateShowInFloater" ), 
true ).toBool() );
 
  391    QAction *action = 
new QAction( tr( 
"Show M value" ), mFloaterActionsMenu );
 
  392    action->setCheckable( 
true );
 
  393    mFloaterActionsMenu->addAction( action );
 
  394    connect( action, &QAction::toggled, 
this, [=]( 
bool checked ) {
 
  397    action->setChecked( 
QgsSettings().value( QStringLiteral( 
"/Cad/MCoordinateShowInFloater" ), 
true ).toBool() );
 
  401    QAction *action = 
new QAction( tr( 
"Show bearing/azimuth" ), mFloaterActionsMenu );
 
  402    action->setCheckable( 
true );
 
  403    mFloaterActionsMenu->addAction( action );
 
  404    connect( action, &QAction::toggled, 
this, [=]( 
bool checked ) {
 
  407    action->setChecked( 
QgsSettings().value( QStringLiteral( 
"/Cad/BearingShowInFloater" ), 
false ).toBool() );
 
  411    QAction *action = 
new QAction( tr( 
"Show common snapping angle" ), mFloaterActionsMenu );
 
  412    action->setCheckable( 
true );
 
  413    mFloaterActionsMenu->addAction( action );
 
  414    connect( action, &QAction::toggled, 
this, [=]( 
bool checked ) {
 
  417    action->setChecked( 
QgsSettings().value( QStringLiteral( 
"/Cad/CommonAngleSnappingShowInFloater" ), 
false ).toBool() );
 
  420  updateCapacity( 
true );
 
  424    mConstructionGuidesLayer.reset();
 
 
  435    mCurrentTool->deleteLater();
 
 
  442    return tr( 
"Do Not Snap to Common Angles" );
 
  444    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 );
 
 
  449  mXLineEdit->setText( value );
 
  452    emit mXLineEdit->returnPressed();
 
  456    QEvent *e = 
new QEvent( QEvent::FocusOut );
 
  457    QCoreApplication::postEvent( mXLineEdit, e );
 
  461    emit mXLineEdit->textEdited( value );
 
 
  466  mYLineEdit->setText( value );
 
  469    emit mYLineEdit->returnPressed();
 
  473    QEvent *e = 
new QEvent( QEvent::FocusOut );
 
  474    QCoreApplication::postEvent( mYLineEdit, e );
 
  478    emit mYLineEdit->textEdited( value );
 
 
  483  mZLineEdit->setText( value );
 
  486    emit mZLineEdit->returnPressed();
 
  490    QEvent *e = 
new QEvent( QEvent::FocusOut );
 
  491    QCoreApplication::postEvent( mZLineEdit, e );
 
  495    emit mZLineEdit->textEdited( value );
 
 
  500  mMLineEdit->setText( value );
 
  503    emit mMLineEdit->returnPressed();
 
  507    QEvent *e = 
new QEvent( QEvent::FocusOut );
 
  508    QCoreApplication::postEvent( mMLineEdit, e );
 
  512    emit mMLineEdit->textEdited( value );
 
 
  517  mAngleLineEdit->setText( value );
 
  520    emit mAngleLineEdit->returnPressed();
 
  524    emit mAngleLineEdit->textEdited( value );
 
 
  529  mDistanceLineEdit->setText( value );
 
  532    emit mDistanceLineEdit->returnPressed();
 
  536    QEvent *e = 
new QEvent( QEvent::FocusOut );
 
  537    QCoreApplication::postEvent( mDistanceLineEdit, e );
 
  541    emit mDistanceLineEdit->textEdited( value );
 
 
  546void QgsAdvancedDigitizingDockWidget::setCadEnabled( 
bool enabled )
 
  548  mCadEnabled = enabled;
 
  549  mEnableAction->setChecked( enabled );
 
  550  mConstructionModeAction->setEnabled( enabled );
 
  551  mSettingsAction->setEnabled( enabled );
 
  552  mInputWidgets->setEnabled( enabled );
 
  553  mFloaterAction->setEnabled( enabled );
 
  554  mConstructionAction->setEnabled( enabled );
 
  555  mToolsAction->setEnabled( enabled );
 
  560    mLineExtensionAction->setChecked( 
false );
 
  561    mXyVertexAction->setChecked( 
false );
 
  563    mParallelAction->setEnabled( 
false );
 
  564    mPerpendicularAction->setEnabled( 
false );
 
  567      mCurrentTool->deleteLater();
 
  574  setConstructionMode( 
false );
 
  590  bool enableZ = 
false;
 
  591  bool enableM = 
false;
 
  595    switch ( layer->type() )
 
  608        QgsMeshLayer *mlayer = qobject_cast<QgsMeshLayer *>( layer );
 
 
  630  mRelativeZButton->setEnabled( 
enable );
 
  631  mZLabel->setEnabled( 
enable );
 
  632  mZLineEdit->setEnabled( 
enable );
 
  633  if ( mZLineEdit->isEnabled() )
 
  637  mLockZButton->setEnabled( 
enable );
 
 
  643  mRelativeMButton->setEnabled( 
enable );
 
  644  mMLabel->setEnabled( 
enable );
 
  645  mMLineEdit->setEnabled( 
enable );
 
  646  if ( mMLineEdit->isEnabled() )
 
  650  mLockMButton->setEnabled( 
enable );
 
 
  654void QgsAdvancedDigitizingDockWidget::activateCad( 
bool enabled )
 
  656  enabled &= mCurrentMapToolSupportsCad;
 
  658  mSessionActive = enabled;
 
  660  if ( enabled && !isVisible() )
 
  665  setCadEnabled( enabled );
 
  672    mCurrentTool->deleteLater();
 
  673    mCurrentTool = 
nullptr;
 
  680    if ( QWidget *toolWidget = mCurrentTool->createWidget() )
 
  682      toolWidget->setParent( mUserInputWidget );
 
 
  691  return mCurrentTool.data();
 
 
  694void QgsAdvancedDigitizingDockWidget::betweenLineConstraintClicked( 
bool activated )
 
  700  else if ( sender() == mParallelAction )
 
  704  else if ( sender() == mPerpendicularAction )
 
  710void QgsAdvancedDigitizingDockWidget::setConstraintRelative( 
bool activate )
 
  712  if ( sender() == mRelativeAngleButton )
 
  714    mAngleConstraint->setRelative( activate );
 
  717  else if ( sender() == mRelativeXButton )
 
  719    mXConstraint->setRelative( activate );
 
  722  else if ( sender() == mRelativeYButton )
 
  724    mYConstraint->setRelative( activate );
 
  727  else if ( sender() == mRelativeZButton )
 
  729    mZConstraint->setRelative( activate );
 
  732  else if ( sender() == mRelativeMButton )
 
  734    mMConstraint->setRelative( activate );
 
  739void QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock( 
bool activate )
 
  741  if ( sender() == mRepeatingLockDistanceButton )
 
  743    mDistanceConstraint->setRepeatingLock( activate );
 
  745  else if ( sender() == mRepeatingLockAngleButton )
 
  747    mAngleConstraint->setRepeatingLock( activate );
 
  749  else if ( sender() == mRepeatingLockXButton )
 
  751    mXConstraint->setRepeatingLock( activate );
 
  753  else if ( sender() == mRepeatingLockYButton )
 
  755    mYConstraint->setRepeatingLock( activate );
 
  757  else if ( sender() == mRepeatingLockZButton )
 
  759    mZConstraint->setRepeatingLock( activate );
 
  761  else if ( sender() == mRepeatingLockMButton )
 
  763    mMConstraint->setRepeatingLock( activate );
 
  767void QgsAdvancedDigitizingDockWidget::setConstructionMode( 
bool enabled )
 
  769  mConstructionMode = enabled;
 
  770  mConstructionModeAction->setChecked( enabled );
 
  774    if ( enabled && mCadPointList.size() > 1 )
 
  776      mConstructionGuideLine.
addVertex( mCadPointList.at( 1 ) );
 
  781void QgsAdvancedDigitizingDockWidget::settingsButtonTriggered( QAction *action )
 
  784  for ( 
auto it = mCommonAngleActions.cbegin(); it != mCommonAngleActions.cend(); ++it )
 
  786    if ( it.value() == action )
 
  788      it.value()->setChecked( 
true );
 
  789      mCommonAngleConstraint = it.key();
 
  791      mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
 
  798QgsMapLayer *QgsAdvancedDigitizingDockWidget::targetLayer()
 const 
  802    return advancedTool->layer();
 
  816  if ( releaseRepeatingLocks )
 
  818    mXyVertexAction->setChecked( 
false );
 
  822    mLineExtensionAction->setChecked( 
false );
 
  829  if ( releaseRepeatingLocks || !mAngleConstraint->isRepeatingLock() )
 
  834  if ( releaseRepeatingLocks || !mDistanceConstraint->isRepeatingLock() )
 
  839  if ( releaseRepeatingLocks || !mXConstraint->isRepeatingLock() )
 
  844  if ( releaseRepeatingLocks || !mYConstraint->isRepeatingLock() )
 
  849  if ( releaseRepeatingLocks || !mZConstraint->isRepeatingLock() )
 
  854  if ( releaseRepeatingLocks || !mMConstraint->isRepeatingLock() )
 
  860  if ( !mCadPointList.empty() )
 
  862    if ( !mXConstraint->isLocked() && !mXConstraint->relative() )
 
  864      mXConstraint->setValue( mCadPointList.constLast().x(), 
true );
 
  866    if ( !mYConstraint->isLocked() && !mYConstraint->relative() )
 
  868      mYConstraint->setValue( mCadPointList.constLast().y(), 
true );
 
  870    if ( !mZConstraint->isLocked() && !mZConstraint->relative() )
 
  872      mZConstraint->setValue( mCadPointList.constLast().z(), 
true );
 
  874    if ( !mMConstraint->isLocked() && !mMConstraint->relative() )
 
  876      mMConstraint->setValue( mCadPointList.constLast().m(), 
true );
 
 
  882void QgsAdvancedDigitizingDockWidget::emit pointChanged()
 
  885  QPoint globalPos = mMapCanvas->cursor().pos();
 
  886  QPoint pos = mMapCanvas->mapFromGlobal( globalPos );
 
  887  QMouseEvent *e = 
new QMouseEvent( QEvent::MouseMove, pos, globalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier );
 
  888  mCurrentMapTool->canvasMoveEvent( e );
 
  894  CadConstraint *constraint = 
nullptr;
 
  895  if ( obj == mAngleLineEdit || obj == mLockAngleButton )
 
  897    constraint = mAngleConstraint.get();
 
  899  else if ( obj == mDistanceLineEdit || obj == mLockDistanceButton )
 
  901    constraint = mDistanceConstraint.get();
 
  903  else if ( obj == mXLineEdit || obj == mLockXButton )
 
  905    constraint = mXConstraint.get();
 
  907  else if ( obj == mYLineEdit || obj == mLockYButton )
 
  909    constraint = mYConstraint.get();
 
  911  else if ( obj == mZLineEdit || obj == mLockZButton )
 
  913    constraint = mZConstraint.get();
 
  915  else if ( obj == mMLineEdit || obj == mLockMButton )
 
  917    constraint = mMConstraint.get();
 
  919  else if ( obj == mLineExtensionAction )
 
  921    constraint = mLineExtensionConstraint.get();
 
  923  else if ( obj == mXyVertexAction )
 
  925    constraint = mXyVertexConstraint.get();
 
  930double QgsAdvancedDigitizingDockWidget::parseUserInput( 
const QString &inputValue, 
const Qgis::CadConstraintType type, 
bool &ok )
 const 
  941    const QVariant result = expr.evaluate();
 
  942    if ( expr.hasEvalError() )
 
  945      QString inputValueC { inputValue };
 
  948      if ( inputValue.contains( QLocale().groupSeparator() ) )
 
  950        inputValueC.remove( QLocale().groupSeparator() );
 
  952        const QVariant resultC = exprC.evaluate();
 
  953        if ( !exprC.hasEvalError() )
 
  955          value = resultC.toDouble( &ok );
 
  960      if ( !ok && QLocale().decimalPoint() != QChar( 
'.' ) && inputValueC.contains( QLocale().decimalPoint() ) )
 
  962        QgsExpression exprC( inputValueC.replace( QLocale().decimalPoint(), QChar( 
'.' ) ) );
 
  963        const QVariant resultC = exprC.evaluate();
 
  964        if ( !exprC.hasEvalError() )
 
  966          value = resultC.toDouble( &ok );
 
  972      value = result.toDouble( &ok );
 
  987void QgsAdvancedDigitizingDockWidget::updateConstraintValue( CadConstraint *constraint, 
const QString &textValue, 
bool convertExpression )
 
  989  if ( !constraint || textValue.isEmpty() )
 
  998  const double value = parseUserInput( textValue, constraint->cadConstraintType(), ok );
 
 1002  constraint->setValue( value, convertExpression );
 
 1007void QgsAdvancedDigitizingDockWidget::lockConstraint( 
bool activate  )
 
 1009  CadConstraint *constraint = objectToConstraint( sender() );
 
 1017    const QString textValue = constraint->lineEdit()->text();
 
 1018    if ( !textValue.isEmpty() )
 
 1021      const double value = parseUserInput( textValue, constraint->cadConstraintType(), ok );
 
 1024        constraint->setValue( value );
 
 1038  if ( constraint == mXConstraint.get() )
 
 1042  else if ( constraint == mYConstraint.get() )
 
 1046  else if ( constraint == mZConstraint.get() )
 
 1050  else if ( constraint == mMConstraint.get() )
 
 1054  else if ( constraint == mDistanceConstraint.get() )
 
 1058  else if ( constraint == mAngleConstraint.get() )
 
 1066    if ( constraint == mAngleConstraint.get() )
 
 1076void QgsAdvancedDigitizingDockWidget::constraintTextEdited( 
const QString &textValue )
 
 1078  CadConstraint *constraint = objectToConstraint( sender() );
 
 1084  updateConstraintValue( constraint, textValue, 
false );
 
 1087void QgsAdvancedDigitizingDockWidget::constraintFocusOut()
 
 1089  QLineEdit *lineEdit = qobject_cast<QLineEdit *>( sender()->parent() );
 
 1093  CadConstraint *constraint = objectToConstraint( lineEdit );
 
 1099  updateConstraintValue( constraint, lineEdit->text(), 
true );
 
 1104  mBetweenLineConstraint = constraint;
 
 1109void QgsAdvancedDigitizingDockWidget::lockParameterlessConstraint( 
bool activate  )
 
 1111  CadConstraint *constraint = objectToConstraint( sender() );
 
 1119  if ( constraint == mXyVertexConstraint.get() )
 
 1123  else if ( constraint == mLineExtensionConstraint.get() )
 
 1137void QgsAdvancedDigitizingDockWidget::updateCapacity( 
bool updateUIwithoutChange )
 
 1143  if ( mCadPointList.count() > 1 )
 
 1146    if ( !isGeographic )
 
 1152  if ( mCadPointList.count() > 2 )
 
 1154    if ( !isGeographic )
 
 1157  if ( !updateUIwithoutChange && newCapacities == mCapacities )
 
 1167  const bool distance = mCadEnabled && newCapacities.testFlag( 
Distance );
 
 1168  const bool relativeAngle = mCadEnabled && newCapacities.testFlag( 
RelativeAngle );
 
 1169  const bool absoluteAngle = mCadEnabled && newCapacities.testFlag( 
AbsoluteAngle );
 
 1170  const bool relativeCoordinates = mCadEnabled && newCapacities.testFlag( 
RelativeCoordinates );
 
 1172  mPerpendicularAction->setEnabled( distance && snappingEnabled );
 
 1173  mParallelAction->setEnabled( distance && snappingEnabled );
 
 1175  mLineExtensionAction->setEnabled( snappingEnabled );
 
 1176  mXyVertexAction->setEnabled( snappingEnabled );
 
 1180  if ( !snappingEnabled )
 
 1182    mPerpendicularAction->setToolTip( tr( 
"Snapping must be enabled to utilize perpendicular mode." ) );
 
 1183    mParallelAction->setToolTip( tr( 
"Snapping must be enabled to utilize parallel mode." ) );
 
 1184    mLineExtensionAction->setToolTip( tr( 
"Snapping must be enabled to utilize line extension mode." ) );
 
 1185    mXyVertexAction->setToolTip( tr( 
"Snapping must be enabled to utilize xy point mode." ) );
 
 1187  else if ( mCadPointList.count() <= 1 )
 
 1189    mPerpendicularAction->setToolTip( tr( 
"A first vertex should be drawn to utilize perpendicular mode." ) );
 
 1190    mParallelAction->setToolTip( tr( 
"A first vertex should be drawn to utilize parallel mode." ) );
 
 1192  else if ( isGeographic )
 
 1194    mPerpendicularAction->setToolTip( tr( 
"Perpendicular mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
 
 1195    mParallelAction->setToolTip( tr( 
"Parallel mode cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
 
 1199    mPerpendicularAction->setToolTip( 
"<b>" + tr( 
"Perpendicular" ) + 
"</b><br>(" + tr( 
"press p to switch between perpendicular, parallel and normal mode" ) + 
")" );
 
 1200    mParallelAction->setToolTip( 
"<b>" + tr( 
"Parallel" ) + 
"</b><br>(" + tr( 
"press p to switch between perpendicular, parallel and normal mode" ) + 
")" );
 
 1204  if ( !absoluteAngle )
 
 1210  mLockAngleButton->setEnabled( absoluteAngle );
 
 1211  mRelativeAngleButton->setEnabled( relativeAngle );
 
 1212  mAngleLineEdit->setEnabled( absoluteAngle );
 
 1214  if ( !absoluteAngle )
 
 1218  if ( !relativeAngle )
 
 1220    mAngleConstraint->setRelative( 
false );
 
 1223  else if ( relativeAngle && !mCapacities.testFlag( 
RelativeAngle ) )
 
 1226    mAngleConstraint->setRelative( 
true );
 
 1231  mLockDistanceButton->setEnabled( distance && relativeCoordinates );
 
 1232  mDistanceLineEdit->setEnabled( distance && relativeCoordinates );
 
 1234  if ( !( distance && relativeCoordinates ) )
 
 1239  mRelativeXButton->setEnabled( relativeCoordinates );
 
 1240  mRelativeYButton->setEnabled( relativeCoordinates );
 
 1241  mRelativeZButton->setEnabled( relativeCoordinates );
 
 1242  mRelativeMButton->setEnabled( relativeCoordinates );
 
 1245  mCapacities = newCapacities;
 
 1253  constr.
locked = 
c->isLocked();
 
 1255  constr.
value = 
c->value();
 
 1262  if ( !mLineExtensionConstraint->isLocked() && !mXyVertexConstraint->isLocked() )
 
 1268  const int lastIndex = mLockedSnapVertices.length() - 1;
 
 1269  for ( 
int i = lastIndex; i >= 0; --i )
 
 1271    if ( mLockedSnapVertices[i].point() == snapMatch.
point() )
 
 1273      if ( snapMatch.
point() != previouslySnap.
point() )
 
 1275        mLockedSnapVertices.removeAt( i );
 
 1281  if ( snapMatch.
point() != previouslySnap.
point() )
 
 1283    mLockedSnapVertices.enqueue( snapMatch );
 
 1286  if ( mLockedSnapVertices.count() > 3 )
 
 1288    mLockedSnapVertices.dequeue();
 
 1297  context.
xConstraint = _constraint( mXConstraint.get() );
 
 1298  context.
yConstraint = _constraint( mYConstraint.get() );
 
 1299  context.
zConstraint = _constraint( mZConstraint.get() );
 
 1300  context.
mConstraint = _constraint( mMConstraint.get() );
 
 1327  const bool res = output.
valid;
 
 1329  mSnappedSegment.clear();
 
 1334    mSnappedSegment << edgePt0 << edgePt1;
 
 1355    mSnapIndicator->setMatch( output.
snapMatch );
 
 1356    mSnapIndicator->setVisible( 
true );
 
 1360    mSnapIndicator->setVisible( 
false );
 
 1382  if ( mSnapMatch.
layer() )
 
 1398    toggleLockedSnapVertex( mSnapMatch, mLastSnapMatch );
 
 1399    mLastSnapMatch = mSnapMatch;
 
 1409  if ( mLockZButton->isChecked() )
 
 1411    point.
setZ( QLocale().toDouble( mZLineEdit->text() ) );
 
 1413  if ( mLockMButton->isChecked() )
 
 1415    point.
setM( QLocale().toDouble( mMLineEdit->text() ) );
 
 1421  updateUnlockedConstraintValues( point );
 
 1429    emit 
pushWarning( tr( 
"Some constraints are incompatible. Resulting point might be incorrect." ) );
 
 
 1436void QgsAdvancedDigitizingDockWidget::updateUnlockedConstraintValues( 
const QgsPoint &point )
 
 1438  bool previousPointExist, penulPointExist;
 
 1443  if ( !mAngleConstraint->isLocked() && previousPointExist )
 
 1445    double prevAngle = 0.0;
 
 1447    if ( penulPointExist && mAngleConstraint->relative() )
 
 1450      prevAngle = std::atan2( previousPt.
y() - penultimatePt.
y(), previousPt.
x() - penultimatePt.
x() ) * 180 / M_PI;
 
 1453    const double xAngle { std::atan2( point.
y() - previousPt.
y(), point.
x() - previousPt.
x() ) * 180 / M_PI };
 
 1456    const double angle = std::fmod( xAngle - prevAngle, 360.0 );
 
 1457    mAngleConstraint->setValue( angle );
 
 1460    double bearing { std::fmod( xAngle, 360.0 ) };
 
 1461    bearing = bearing <= 90.0 ? 90.0 - bearing : ( bearing > 90 ? 270.0 + 180.0 - bearing : 270.0 - bearing );
 
 1467  if ( !mDistanceConstraint->isLocked() && previousPointExist )
 
 1469    mDistanceConstraint->setValue( std::sqrt( previousPt.
distanceSquared( point ) ) );
 
 1472  if ( !mXConstraint->isLocked() )
 
 1474    if ( previousPointExist && mXConstraint->relative() )
 
 1476      mXConstraint->setValue( point.
x() - previousPt.
x() );
 
 1480      mXConstraint->setValue( point.
x() );
 
 1484  if ( !mYConstraint->isLocked() )
 
 1486    if ( previousPointExist && mYConstraint->relative() )
 
 1488      mYConstraint->setValue( point.
y() - previousPt.
y() );
 
 1492      mYConstraint->setValue( point.
y() );
 
 1496  if ( !mZConstraint->isLocked() )
 
 1498    if ( previousPointExist && mZConstraint->relative() )
 
 1500      mZConstraint->setValue( point.
z() - previousPt.
z() );
 
 1504      mZConstraint->setValue( point.
z() );
 
 1508  if ( !mMConstraint->isLocked() )
 
 1510    if ( previousPointExist && mMConstraint->relative() )
 
 1512      mMConstraint->setValue( point.
m() - previousPt.
m() );
 
 1516      mMConstraint->setValue( point.
m() );
 
 1522QList<QgsPointXY> QgsAdvancedDigitizingDockWidget::snapSegmentToAllLayers( 
const QgsPointXY &originalMapPoint, 
bool *snapped )
 const 
 1535  snappingUtils->
setConfig( localConfig );
 
 1537  match = snappingUtils->
snapToMap( originalMapPoint, 
nullptr, 
true );
 
 1539  snappingUtils->
setConfig( canvasConfig );
 
 1549    *snapped = 
segment.count() == 2;
 
 1559    mCurrentTool->canvasPressEvent( event );
 
 1564    event->setAccepted( 
false );
 
 
 1576    mCurrentTool->canvasMoveEvent( event );
 
 
 1584  if ( event->button() == Qt::RightButton )
 
 1588      mCurrentTool->canvasReleaseEvent( event );
 
 1589      if ( !event->isAccepted() )
 
 1601      event->setAccepted( 
false );
 
 1607      mCurrentTool->canvasReleaseEvent( event );
 
 1608      if ( !event->isAccepted() )
 
 1619        if ( mLockZButton->isChecked() )
 
 1621          point.
setZ( QLocale().toDouble( mZLineEdit->text() ) );
 
 1623        if ( mLockMButton->isChecked() )
 
 1625          point.
setM( QLocale().toDouble( mMLineEdit->text() ) );
 
 1636      event->setAccepted( 
false );
 
 
 1648  bool previousPointExist, penulPointExist, snappedSegmentExist;
 
 1651  mSnappedSegment = snapSegmentToAllLayers( e->
originalMapPoint(), &snappedSegmentExist );
 
 1653  if ( !previousPointExist || !snappedSegmentExist )
 
 1658  double angle = std::atan2( mSnappedSegment[0].y() - mSnappedSegment[1].y(), mSnappedSegment[0].x() - mSnappedSegment[1].x() );
 
 1660  if ( mAngleConstraint->relative() && penulPointExist )
 
 1662    angle -= std::atan2( previousPt.
y() - penultimatePt.
y(), previousPt.
x() - penultimatePt.
x() );
 
 1670  angle *= 180 / M_PI;
 
 1672  mAngleConstraint->setValue( angle );
 
 1673  mAngleConstraint->setLockMode( lockMode );
 
 
 1691    case Qt::Key_Backspace:
 
 1692    case Qt::Key_Delete:
 
 1698    case Qt::Key_Escape:
 
 
 1717    mCurrentTool->deleteLater();
 
 1720  if ( !mConstructionGuideLine.
isEmpty() )
 
 1722    mConstructionGuideLine.
clear();
 
 
 1738    case Qt::Key_Backspace:
 
 1739    case Qt::Key_Delete:
 
 1745    case Qt::Key_Escape:
 
 1749      if ( mConstructionGuideLine.
numPoints() >= 2 )
 
 1751        mConstructionGuidesLayer->dataProvider()->deleteFeatures( 
QgsFeatureIds() << mConstructionGuideId );
 
 1752        mConstructionGuideLine.
clear();
 
 1757        mCurrentTool->deleteLater();
 
 1764      filterKeyPress( e );
 
 
 1773  const auto constPoints = points;
 
 
 1782  mDistanceConstraint->toggleLocked();
 
 
 1787bool QgsAdvancedDigitizingDockWidget::eventFilter( QObject *obj, QEvent *event )
 
 1791    return QgsDockWidget::eventFilter( obj, event );
 
 1799  if ( event->type() == QEvent::ShortcutOverride || event->type() == QEvent::KeyPress )
 
 1801    if ( QKeyEvent *keyEvent = 
dynamic_cast<QKeyEvent *
>( event ) )
 
 1803      return filterKeyPress( keyEvent );
 
 1806  return QgsDockWidget::eventFilter( obj, event );
 
 1809bool QgsAdvancedDigitizingDockWidget::filterKeyPress( QKeyEvent *e )
 
 1815  const QEvent::Type type = e->type();
 
 1818    case Qt::Key_Escape:
 
 1820      if ( type == QEvent::KeyPress && mCurrentTool )
 
 1822        mCurrentTool->deleteLater();
 
 1824      else if ( type == QEvent::KeyPress && mConstructionMode && mConstructionGuideLine.
numPoints() >= 2 )
 
 1826        mConstructionGuidesLayer->dataProvider()->deleteFeatures( 
QgsFeatureIds() << mConstructionGuideId );
 
 1827        mConstructionGuideLine.
clear();
 
 1829        if ( mCadPointList.size() > 1 )
 
 1831          mConstructionGuideLine.
addVertex( mCadPointList.at( 1 ) );
 
 1846      if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
 
 1848        mXConstraint->toggleLocked();
 
 1853      else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
 
 1857          mXConstraint->toggleRelative();
 
 1864      else if ( type == QEvent::KeyPress )
 
 1866        mXLineEdit->setFocus();
 
 1867        mXLineEdit->selectAll();
 
 1876      if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
 
 1878        mYConstraint->toggleLocked();
 
 1883      else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
 
 1887          mYConstraint->toggleRelative();
 
 1894      else if ( type == QEvent::KeyPress )
 
 1896        mYLineEdit->setFocus();
 
 1897        mYLineEdit->selectAll();
 
 1906      if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::AltModifier )
 
 1908        mZConstraint->toggleLocked();
 
 1913      else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
 
 1917          mZConstraint->toggleRelative();
 
 1924      else if ( type == QEvent::KeyPress )
 
 1926        mZLineEdit->setFocus();
 
 1927        mZLineEdit->selectAll();
 
 1936      if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
 
 1938        mMConstraint->toggleLocked();
 
 1943      else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
 
 1947          mMConstraint->toggleRelative();
 
 1954      else if ( type == QEvent::KeyPress )
 
 1956        mMLineEdit->setFocus();
 
 1957        mMLineEdit->selectAll();
 
 1966      if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
 
 1970          mAngleConstraint->toggleLocked();
 
 1976      else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
 
 1980          mAngleConstraint->toggleRelative();
 
 1987      else if ( type == QEvent::KeyPress )
 
 1989        mAngleLineEdit->setFocus();
 
 1990        mAngleLineEdit->selectAll();
 
 1999      if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
 
 2008      else if ( type == QEvent::KeyPress )
 
 2010        mDistanceLineEdit->setFocus();
 
 2011        mDistanceLineEdit->selectAll();
 
 2019      if ( type == QEvent::KeyPress )
 
 2021        setConstructionMode( !mConstructionMode );
 
 2028      if ( type == QEvent::KeyPress )
 
 2030        const bool parallel = mParallelAction->isChecked();
 
 2031        const bool perpendicular = mPerpendicularAction->isChecked();
 
 2033        if ( !parallel && !perpendicular )
 
 2037        else if ( perpendicular )
 
 2054      if ( type == QEvent::ShortcutOverride )
 
 2056        const QList<double> constActionKeys { mCommonAngleActions.keys() };
 
 2057        const int currentAngleActionIndex { 
static_cast<int>( constActionKeys.indexOf( mCommonAngleConstraint ) ) };
 
 2058        const QList<QAction *> constActions { mCommonAngleActions.values() };
 
 2059        QAction *nextAngleAction;
 
 2060        if ( e->modifiers() == Qt::ShiftModifier )
 
 2062          nextAngleAction = currentAngleActionIndex == 0 ? constActions.last() : constActions.at( currentAngleActionIndex - 1 );
 
 2066          nextAngleAction = currentAngleActionIndex == constActions.count() - 1 ? constActions.first() : constActions.at( currentAngleActionIndex + 1 );
 
 2068        nextAngleAction->trigger();
 
 2078  return e->isAccepted();
 
 2087    mAngleLineEdit->setToolTip( tr( 
"Angle constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
 
 2088    mDistanceLineEdit->setToolTip( tr( 
"Distance constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
 
 2090    mLabelX->setText( tr( 
"Long" ) );
 
 2091    mLabelY->setText( tr( 
"Lat" ) );
 
 2093    mXConstraint->setPrecision( 8 );
 
 2094    mYConstraint->setPrecision( 8 );
 
 2098    mAngleLineEdit->setToolTip( 
"<b>" + tr( 
"Angle" ) + 
"</b><br>(" + tr( 
"press a for quick access" ) + 
")" );
 
 2099    mAngleLineEdit->setToolTip( QString() );
 
 2101    mDistanceLineEdit->setToolTip( 
"<b>" + tr( 
"Distance" ) + 
"</b><br>(" + tr( 
"press d for quick access" ) + 
")" );
 
 2103    mLabelX->setText( tr( 
"x" ) );
 
 2104    mLabelY->setText( tr( 
"y" ) );
 
 2106    mXConstraint->setPrecision( 6 );
 
 2107    mYConstraint->setPrecision( 6 );
 
 2112  mEnableAction->setEnabled( 
true );
 
 2113  mErrorLabel->hide();
 
 2116  mCurrentMapToolSupportsCad = 
true;
 
 2118  if ( mSessionActive && !isVisible() )
 
 2123  setCadEnabled( mSessionActive );
 
 2125  if ( !mConstructionGuidesLayer )
 
 2127    resetConstructionGuides();
 
 2130  if ( mDeferredUpdateConstructionGuidesCrs )
 
 2132    updateConstructionGuidesCrs();
 
 
 2142  mEnableAction->setEnabled( 
false );
 
 2143  mErrorLabel->setText( tr( 
"Advanced digitizing tools are not enabled for the current map tool" ) );
 
 2144  mErrorLabel->show();
 
 2147  mCurrentMapToolSupportsCad = 
false;
 
 2149  mSnapIndicator->setVisible( 
false );
 
 2151  setCadEnabled( 
false );
 
 
 2156  mCadPaintItem->update();
 
 
 2161  if ( !force && ( mLineExtensionConstraint->isLocked() || mXyVertexConstraint->isLocked() ) )
 
 2166  mLockedSnapVertices.clear();
 
 
 2171  QgsPoint pt = pointXYToPoint( point );
 
 2174    mCadPointList << pt;
 
 2178    mCadPointList.insert( 0, pt );
 
 2187      if ( mConstructionGuideLine.
numPoints() == 2 )
 
 2192        mConstructionGuidesLayer->dataProvider()->addFeature( feature );
 
 2193        mConstructionGuideId = feature.
id();
 
 2195      else if ( mConstructionGuideLine.
numPoints() > 2 )
 
 2198        mConstructionGuidesLayer->dataProvider()->changeGeometryValues( { { mConstructionGuideId, geom } } );
 
 2203      if ( !mConstructionGuideLine.
isEmpty() )
 
 2208        mConstructionGuidesLayer->dataProvider()->changeGeometryValues( { { mConstructionGuideId, geom } } );
 
 2209        mConstructionGuideLine.
clear();
 
 
 2224  mCadPointList.removeAt( i );
 
 
 2231  mCadPointList.clear();
 
 2232  mSnappedSegment.clear();
 
 
 2242    mCadPointList << point;
 
 2247    mCadPointList[0] = point;
 
 
 2254  if ( mode == mLockMode )
 
 2259  mLockerButton->setChecked( mode == 
HardLock );
 
 2260  if ( mRepeatingLockButton )
 
 2264      mRepeatingLockButton->setEnabled( 
true );
 
 2268      mRepeatingLockButton->setChecked( 
false );
 
 2269      mRepeatingLockButton->setEnabled( 
false );
 
 
 2281  mRepeatingLock = repeating;
 
 2282  if ( mRepeatingLockButton )
 
 2283    mRepeatingLockButton->setChecked( repeating );
 
 
 2288  mRelative = relative;
 
 2289  if ( mRelativeButton )
 
 2291    mRelativeButton->setChecked( relative );
 
 
 2298  if ( updateWidget && mLineEdit->isEnabled() )
 
 2299    mLineEdit->setText( displayValue() );
 
 
 2304  switch ( mCadConstraintType )
 
 2308      return QLocale().toString( mValue, 
'f', mPrecision ).append( tr( 
" °" ) );
 
 2315        return QLocale().toString( mValue, 
'f', mPrecision ).append( tr( 
" °" ) );
 
 2319        return QLocale().toString( mValue, 
'f', mPrecision );
 
 2336  return QLocale().toString( mValue, 
'f', mPrecision );
 
 
 2341  setLockMode( mLockMode == HardLock ? NoLock : HardLock );
 
 
 2346  setRelative( !mRelative );
 
 
 2352  if ( mLineEdit->isEnabled() )
 
 2353    mLineEdit->setText( displayValue() );
 
 
 2358  return mCadConstraintType;
 
 
 2363  mCadConstraintType = constraintType;
 
 
 2368  mMapCanvas = mapCanvas;
 
 
 2373  QString value { text.trimmed() };
 
 2374  switch ( constraintType )
 
 2380      if ( value.endsWith( distanceUnit ) )
 
 2382        value.chop( distanceUnit.length() );
 
 2389      const QString angleUnit { tr( 
"°" ) };
 
 2390      if ( value.endsWith( angleUnit ) )
 
 2392        value.chop( angleUnit.length() );
 
 2399  return value.trimmed();
 
 
 2407    return mCadPointList.value( 0 );
 
 
 2416    QgsPoint res = mCadPointList.value( 0 );
 
 2418    res.
setX( layerCoordinates.
x() );
 
 2419    res.
setY( layerCoordinates.
y() );
 
 
 2430    return mCadPointList.value( 1 );
 
 
 2440    return mCadPointList.value( 2 );
 
 
 2445QgsPoint QgsAdvancedDigitizingDockWidget::pointXYToPoint( 
const QgsPointXY &point )
 const 
 2452  return mZLineEdit->isEnabled() ? QLocale().toDouble( mZLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
 
 
 2457  return mMLineEdit->isEnabled() ? QLocale().toDouble( mMLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
 
 
 2462  return mShowConstructionGuides ? mShowConstructionGuides->isChecked() : 
false;
 
 
 2467  return mSnapToConstructionGuides ? mShowConstructionGuides->isChecked() && mSnapToConstructionGuides->isChecked() : 
false;
 
 
 2472  return mRecordConstructionGuides ? mRecordConstructionGuides->isChecked() : 
false;
 
 
 2475void QgsAdvancedDigitizingDockWidget::updateConstructionGuidesCrs()
 
 2477  if ( !mConstructionGuidesLayer )
 
 2484    mDeferredUpdateConstructionGuidesCrs = 
true;
 
 2495    mConstructionGuidesLayer->dataProvider()->changeGeometryValues( { { feature.
id(), geom } } );
 
 2498  mDeferredUpdateConstructionGuidesCrs = 
false;
 
 2501void QgsAdvancedDigitizingDockWidget::resetConstructionGuides()
 
 2503  if ( mConstructionGuidesLayer )
 
 2505    mConstructionGuidesLayer.reset();
 
 2509  mConstructionGuidesLayer = std::make_unique<QgsVectorLayer>( QStringLiteral( 
"LineString?crs=%1" ).arg( mMapCanvas->
mapSettings().
destinationCrs().
authid() ), QStringLiteral( 
"constructionGuides" ), QStringLiteral( 
"memory" ), options );
 
DistanceUnit
Units of distance.
 
CadConstraintType
Advanced digitizing constraint type.
 
@ Distance
Distance value.
 
@ YCoordinate
Y Coordinate value.
 
@ XCoordinate
X Coordinate value.
 
@ Group
Composite group layer. Added in QGIS 3.24.
 
@ Plugin
Plugin based layer.
 
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
 
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
 
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
 
@ Mesh
Mesh layer. Added in QGIS 3.2.
 
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
 
@ AllLayers
On all vector layers.
 
BetweenLineConstraint
Between line constraints which can be enabled.
 
@ NoConstraint
No additional constraint.
 
@ Perpendicular
Perpendicular.
 
WkbType
The WKB type describes the number of dimensions a geometry has.
 
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 setItemVisibility(const QgsAdvancedDigitizingFloater::FloaterItem &item, bool visible)
Set whether the floater item should be visible or not.
 
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.
 
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.
 
Class for parsing and evaluation of expressions (formerly called "search strings").
 
Wrapper for iterator of features from vector data provider or vector layer.
 
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
 
This class wraps a request for features to a vector layer (or directly its vector data provider).
 
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.
 
A event filter for watching for focus events on a parent object.
 
void focusIn()
Emitted when parent object gains focus.
 
void focusOut()
Emitted when parent object loses focus.
 
A geometry is the spatial representation of a feature.
 
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
 
static QgsGui * instance()
Returns a pointer to the singleton instance.
 
static QgsAdvancedDigitizingToolsRegistry * advancedDigitizingToolsRegistry()
Returns the global advanced digitizing tools registry, used for registering advanced digitizing tools...
 
void clear() override
Clears the geometry, ie reset it to a null geometry.
 
bool isEmpty() const override
Returns true if the geometry is empty.
 
int numPoints() const override
Returns the number of points in the curve.
 
void addVertex(const QgsPoint &pt)
Adds a new vertex to the end of the line string.
 
QgsLineString * clone() const override
Clones the geometry by performing a deep copy.
 
Map canvas is a class for displaying all GIS data types on a canvas.
 
QgsMapTool * mapTool() const
Returns the currently active tool.
 
QgsSnappingUtils * snappingUtils() const
Returns snapping utility class that is associated with map canvas.
 
void destinationCrsChanged()
Emitted when map CRS has changed.
 
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.
 
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
 
Qgis::DistanceUnit mapUnits() const
Returns the units of the map's geographical coordinates - used for scale calculation.
 
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 context for numeric formats.
 
A class to represent a 2D point.
 
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
 
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
 
bool setValue(const T &value, const QString &dynamicKeyPart=QString()) const
Set settings value.
 
A boolean settings entry.
 
static QgsSettingsTreeNode * sTreeDigitizing
 
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
 
This class has all the configuration of snapping and can return answers to snapping queries.
 
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 data sets.
 
Q_INVOKABLE Qgis::WkbType wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
 
static bool hasZ(Qgis::WkbType type)
Tests whether a WKB type contains the z-dimension.
 
static 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)
 
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.
 
Setting options for loading vector layers.