QGIS API Documentation  3.24.2-Tisler (13c1a02865)
qgsadvanceddigitizingdockwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsadvanceddigitizingdockwidget.cpp - dock for CAD tools
3  ----------------------
4  begin : October 2014
5  copyright : (C) Denis Rouzaud
6  email : [email protected]
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include <QMenu>
17 #include <QEvent>
18 #include <QCoreApplication>
19 
20 #include <cmath>
21 
25 #include "qgsapplication.h"
26 #include "qgscadutils.h"
27 #include "qgsexpression.h"
28 #include "qgslogger.h"
29 #include "qgsmapcanvas.h"
30 #include "qgsmaptooledit.h"
31 #include "qgsmaptoolcapture.h"
33 #include "qgsmessagebaritem.h"
34 #include "qgslinestring.h"
35 #include "qgsfocuswatcher.h"
36 #include "qgssettings.h"
37 #include "qgssnappingutils.h"
38 #include "qgsproject.h"
39 #include "qgsmapmouseevent.h"
40 #include "qgsmessagelog.h"
41 #include "qgsmeshlayer.h"
42 
43 #include <QActionGroup>
44 
45 
47  : QgsDockWidget( parent )
48  , mMapCanvas( canvas )
49  , mSnapIndicator( std::make_unique< QgsSnapIndicator>( canvas ) )
50  , mCommonAngleConstraint( QgsSettings().value( QStringLiteral( "/Cad/CommonAngle" ), 0.0 ).toDouble() )
51 {
52  setupUi( this );
53 
54  mCadPaintItem = new QgsAdvancedDigitizingCanvasItem( canvas, this );
55 
56  mAngleConstraint.reset( new CadConstraint( mAngleLineEdit, mLockAngleButton, mRelativeAngleButton, mRepeatingLockAngleButton ) );
57  mDistanceConstraint.reset( new CadConstraint( mDistanceLineEdit, mLockDistanceButton, nullptr, mRepeatingLockDistanceButton ) );
58  mXConstraint.reset( new CadConstraint( mXLineEdit, mLockXButton, mRelativeXButton, mRepeatingLockXButton ) );
59  mYConstraint.reset( new CadConstraint( mYLineEdit, mLockYButton, mRelativeYButton, mRepeatingLockYButton ) );
60  mZConstraint.reset( new CadConstraint( mZLineEdit, mLockZButton, mRelativeZButton, mRepeatingLockZButton ) );
61  mMConstraint.reset( new CadConstraint( mMLineEdit, mLockMButton, mRelativeMButton, mRepeatingLockMButton ) );
62  mAdditionalConstraint = AdditionalConstraint::NoConstraint;
63 
64  mMapCanvas->installEventFilter( this );
65  mAngleLineEdit->installEventFilter( this );
66  mDistanceLineEdit->installEventFilter( this );
67  mXLineEdit->installEventFilter( this );
68  mYLineEdit->installEventFilter( this );
69  mZLineEdit->installEventFilter( this );
70  mMLineEdit->installEventFilter( this );
71 
72  // Connect the UI to the event filter to update constraints
73  connect( mEnableAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::activateCad );
74  connect( mConstructionModeAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::setConstructionMode );
75  connect( mParallelAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::additionalConstraintClicked );
76  connect( mPerpendicularAction, &QAction::triggered, this, &QgsAdvancedDigitizingDockWidget::additionalConstraintClicked );
77  connect( mLockAngleButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
78  connect( mLockDistanceButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
79  connect( mLockXButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
80  connect( mLockYButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
81  connect( mLockZButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
82  connect( mLockMButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::lockConstraint );
83  connect( mRelativeAngleButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
84  connect( mRelativeXButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
85  connect( mRelativeYButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
86  connect( mRelativeZButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
87  connect( mRelativeMButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRelative );
88  connect( mRepeatingLockDistanceButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
89  connect( mRepeatingLockAngleButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
90  connect( mRepeatingLockXButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
91  connect( mRepeatingLockYButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
92  connect( mRepeatingLockZButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
93  connect( mRepeatingLockMButton, &QAbstractButton::clicked, this, &QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock );
94  connect( mAngleLineEdit, &QLineEdit::returnPressed, this, [ = ]() { lockConstraint(); } );
95  connect( mDistanceLineEdit, &QLineEdit::returnPressed, this, [ = ]() { lockConstraint(); } );
96  connect( mXLineEdit, &QLineEdit::returnPressed, this, [ = ]() { lockConstraint(); } );
97  connect( mYLineEdit, &QLineEdit::returnPressed, this, [ = ]() { lockConstraint(); } );
98  connect( mZLineEdit, &QLineEdit::returnPressed, this, [ = ]() { lockConstraint(); } );
99  connect( mMLineEdit, &QLineEdit::returnPressed, this, [ = ]() { lockConstraint(); } );
100  connect( mAngleLineEdit, &QLineEdit::textEdited, this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
101  connect( mDistanceLineEdit, &QLineEdit::textEdited, this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
102  connect( mXLineEdit, &QLineEdit::textEdited, this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
103  connect( mYLineEdit, &QLineEdit::textEdited, this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
104  connect( mZLineEdit, &QLineEdit::textEdited, this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
105  connect( mMLineEdit, &QLineEdit::textEdited, this, &QgsAdvancedDigitizingDockWidget::constraintTextEdited );
106  //also watch for focus out events on these widgets
107  QgsFocusWatcher *angleWatcher = new QgsFocusWatcher( mAngleLineEdit );
108  connect( angleWatcher, &QgsFocusWatcher::focusOut, this, &QgsAdvancedDigitizingDockWidget::constraintFocusOut );
109  QgsFocusWatcher *distanceWatcher = new QgsFocusWatcher( mDistanceLineEdit );
110  connect( distanceWatcher, &QgsFocusWatcher::focusOut, this, &QgsAdvancedDigitizingDockWidget::constraintFocusOut );
111  QgsFocusWatcher *xWatcher = new QgsFocusWatcher( mXLineEdit );
112  connect( xWatcher, &QgsFocusWatcher::focusOut, this, &QgsAdvancedDigitizingDockWidget::constraintFocusOut );
113  QgsFocusWatcher *yWatcher = new QgsFocusWatcher( mYLineEdit );
114  connect( yWatcher, &QgsFocusWatcher::focusOut, this, &QgsAdvancedDigitizingDockWidget::constraintFocusOut );
115  QgsFocusWatcher *zWatcher = new QgsFocusWatcher( mZLineEdit );
116  connect( zWatcher, &QgsFocusWatcher::focusOut, this, &QgsAdvancedDigitizingDockWidget::constraintFocusOut );
117  QgsFocusWatcher *mWatcher = new QgsFocusWatcher( mMLineEdit );
118  connect( mWatcher, &QgsFocusWatcher::focusOut, this, &QgsAdvancedDigitizingDockWidget::constraintFocusOut );
119 
120  // config menu
121  QMenu *menu = new QMenu( this );
122  // common angles
123  QActionGroup *angleButtonGroup = new QActionGroup( menu ); // actions are exclusive for common angles
124  mCommonAngleActions = QMap<QAction *, double>();
125  QList< QPair< double, QString > > commonAngles;
126  QString menuText;
127  const QList<double> anglesDouble( { 0.0, 5.0, 10.0, 15.0, 18.0, 22.5, 30.0, 45.0, 90.0} );
128  for ( QList<double>::const_iterator it = anglesDouble.constBegin(); it != anglesDouble.constEnd(); ++it )
129  {
130  if ( *it == 0 )
131  menuText = tr( "Do Not Snap to Common Angles" );
132  else
133  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 );
134  commonAngles << QPair<double, QString>( *it, menuText );
135  }
136  for ( QList< QPair<double, QString > >::const_iterator it = commonAngles.constBegin(); it != commonAngles.constEnd(); ++it )
137  {
138  QAction *action = new QAction( it->second, menu );
139  action->setCheckable( true );
140  action->setChecked( it->first == mCommonAngleConstraint );
141  menu->addAction( action );
142  angleButtonGroup->addAction( action );
143  mCommonAngleActions.insert( action, it->first );
144  }
145 
146  qobject_cast< QToolButton *>( mToolbar->widgetForAction( mSettingsAction ) )->setPopupMode( QToolButton::InstantPopup );
147  mSettingsAction->setMenu( menu );
148  mSettingsAction->setCheckable( true );
149  mSettingsAction->setToolTip( tr( "Snap to common angles" ) );
150  mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
151  connect( menu, &QMenu::triggered, this, &QgsAdvancedDigitizingDockWidget::settingsButtonTriggered );
152 
153  // set tooltips
154  mConstructionModeAction->setToolTip( "<b>" + tr( "Construction mode" ) + "</b><br>(" + tr( "press c to toggle on/off" ) + ")" );
155  mDistanceLineEdit->setToolTip( "<b>" + tr( "Distance" ) + "</b><br>(" + tr( "press d for quick access" ) + ")" );
156  mLockDistanceButton->setToolTip( "<b>" + tr( "Lock distance" ) + "</b><br>(" + tr( "press Ctrl + d for quick access" ) + ")" );
157  mRepeatingLockDistanceButton->setToolTip( "<b>" + tr( "Continuously lock distance" ) + "</b>" );
158 
159  mRelativeAngleButton->setToolTip( "<b>" + tr( "Toggles relative angle to previous segment" ) + "</b><br>(" + tr( "press Shift + a for quick access" ) + ")" );
160  mAngleLineEdit->setToolTip( "<b>" + tr( "Angle" ) + "</b><br>(" + tr( "press a for quick access" ) + ")" );
161  mLockAngleButton->setToolTip( "<b>" + tr( "Lock angle" ) + "</b><br>(" + tr( "press Ctrl + a for quick access" ) + ")" );
162  mRepeatingLockAngleButton->setToolTip( "<b>" + tr( "Continuously lock angle" ) + "</b>" );
163 
164  mRelativeXButton->setToolTip( "<b>" + tr( "Toggles relative x to previous node" ) + "</b><br>(" + tr( "press Shift + x for quick access" ) + ")" );
165  mXLineEdit->setToolTip( "<b>" + tr( "X coordinate" ) + "</b><br>(" + tr( "press x for quick access" ) + ")" );
166  mLockXButton->setToolTip( "<b>" + tr( "Lock x coordinate" ) + "</b><br>(" + tr( "press Ctrl + x for quick access" ) + ")" );
167  mRepeatingLockXButton->setToolTip( "<b>" + tr( "Continuously lock x coordinate" ) + "</b>" );
168 
169  mRelativeYButton->setToolTip( "<b>" + tr( "Toggles relative y to previous node" ) + "</b><br>(" + tr( "press Shift + y for quick access" ) + ")" );
170  mYLineEdit->setToolTip( "<b>" + tr( "Y coordinate" ) + "</b><br>(" + tr( "press y for quick access" ) + ")" );
171  mLockYButton->setToolTip( "<b>" + tr( "Lock y coordinate" ) + "</b><br>(" + tr( "press Ctrl + y for quick access" ) + ")" );
172  mRepeatingLockYButton->setToolTip( "<b>" + tr( "Continuously lock y coordinate" ) + "</b>" );
173 
174  mRelativeZButton->setToolTip( "<b>" + tr( "Toggles relative z to previous node" ) + "</b><br>(" + tr( "press Shift + z for quick access" ) + ")" );
175  mZLineEdit->setToolTip( "<b>" + tr( "Z coordinate" ) + "</b><br>(" + tr( "press z for quick access" ) + ")" );
176  mLockZButton->setToolTip( "<b>" + tr( "Lock z coordinate" ) + "</b><br>(" + tr( "press Ctrl + z for quick access" ) + ")" );
177  mRepeatingLockZButton->setToolTip( "<b>" + tr( "Continuously lock z coordinate" ) + "</b>" );
178 
179  mRelativeMButton->setToolTip( "<b>" + tr( "Toggles relative m to previous node" ) + "</b><br>(" + tr( "press Shift + m for quick access" ) + ")" );
180  mMLineEdit->setToolTip( "<b>" + tr( "M coordinate" ) + "</b><br>(" + tr( "press m for quick access" ) + ")" );
181  mLockMButton->setToolTip( "<b>" + tr( "Lock m coordinate" ) + "</b><br>(" + tr( "press Ctrl + m for quick access" ) + ")" );
182  mRepeatingLockMButton->setToolTip( "<b>" + tr( "Continuously lock m coordinate" ) + "</b>" );
183 
184  // Create the slots/signals
185  connect( mXLineEdit, &QLineEdit::textChanged, this, &QgsAdvancedDigitizingDockWidget::valueXChanged );
186  connect( mYLineEdit, &QLineEdit::textChanged, this, &QgsAdvancedDigitizingDockWidget::valueYChanged );
187  connect( mZLineEdit, &QLineEdit::textChanged, this, &QgsAdvancedDigitizingDockWidget::valueZChanged );
188  connect( mMLineEdit, &QLineEdit::textChanged, this, &QgsAdvancedDigitizingDockWidget::valueMChanged );
189  connect( mDistanceLineEdit, &QLineEdit::textChanged, this, &QgsAdvancedDigitizingDockWidget::valueDistanceChanged );
190  connect( mAngleLineEdit, &QLineEdit::textChanged, this, &QgsAdvancedDigitizingDockWidget::valueAngleChanged );
191 
192  // Create the floater
193  mFloater = new QgsAdvancedDigitizingFloater( canvas, this );
194  connect( mToggleFloaterAction, &QAction::triggered, mFloater, &QgsAdvancedDigitizingFloater::setActive );
195  mToggleFloaterAction->setChecked( mFloater->active() );
196 
197  updateCapacity( true );
198  connect( QgsProject::instance(), &QgsProject::snappingConfigChanged, this, [ = ] { updateCapacity( true ); } );
199 
200  disable();
201 }
202 
203 void QgsAdvancedDigitizingDockWidget::setX( const QString &value, WidgetSetMode mode )
204 {
205  mXLineEdit->setText( value );
206  if ( mode == WidgetSetMode::ReturnPressed )
207  {
208  mXLineEdit->returnPressed();
209  }
210  else if ( mode == WidgetSetMode::FocusOut )
211  {
212  QEvent *e = new QEvent( QEvent::FocusOut );
213  QCoreApplication::postEvent( mXLineEdit, e );
214  }
215  else if ( mode == WidgetSetMode::TextEdited )
216  {
217  mXLineEdit->textEdited( value );
218  }
219 }
220 void QgsAdvancedDigitizingDockWidget::setY( const QString &value, WidgetSetMode mode )
221 {
222  mYLineEdit->setText( value );
223  if ( mode == WidgetSetMode::ReturnPressed )
224  {
225  mYLineEdit->returnPressed();
226  }
227  else if ( mode == WidgetSetMode::FocusOut )
228  {
229  QEvent *e = new QEvent( QEvent::FocusOut );
230  QCoreApplication::postEvent( mYLineEdit, e );
231  }
232  else if ( mode == WidgetSetMode::TextEdited )
233  {
234  mYLineEdit->textEdited( value );
235  }
236 }
237 void QgsAdvancedDigitizingDockWidget::setZ( const QString &value, WidgetSetMode mode )
238 {
239  mZLineEdit->setText( value );
240  if ( mode == WidgetSetMode::ReturnPressed )
241  {
242  mZLineEdit->returnPressed();
243  }
244  else if ( mode == WidgetSetMode::FocusOut )
245  {
246  QEvent *e = new QEvent( QEvent::FocusOut );
247  QCoreApplication::postEvent( mZLineEdit, e );
248  }
249  else if ( mode == WidgetSetMode::TextEdited )
250  {
251  mZLineEdit->textEdited( value );
252  }
253 }
254 void QgsAdvancedDigitizingDockWidget::setM( const QString &value, WidgetSetMode mode )
255 {
256  mMLineEdit->setText( value );
257  if ( mode == WidgetSetMode::ReturnPressed )
258  {
259  mMLineEdit->returnPressed();
260  }
261  else if ( mode == WidgetSetMode::FocusOut )
262  {
263  QEvent *e = new QEvent( QEvent::FocusOut );
264  QCoreApplication::postEvent( mMLineEdit, e );
265  }
266  else if ( mode == WidgetSetMode::TextEdited )
267  {
268  mMLineEdit->textEdited( value );
269  }
270 }
272 {
273  mAngleLineEdit->setText( value );
274  if ( mode == WidgetSetMode::ReturnPressed )
275  {
276  mAngleLineEdit->returnPressed();
277  }
278  else if ( mode == WidgetSetMode::FocusOut )
279  {
280  mAngleLineEdit->textEdited( value );
281  }
282 }
284 {
285  mDistanceLineEdit->setText( value );
286  if ( mode == WidgetSetMode::ReturnPressed )
287  {
288  mDistanceLineEdit->returnPressed();
289  }
290  else if ( mode == WidgetSetMode::FocusOut )
291  {
292  QEvent *e = new QEvent( QEvent::FocusOut );
293  QCoreApplication::postEvent( mDistanceLineEdit, e );
294  }
295  else if ( mode == WidgetSetMode::TextEdited )
296  {
297  mDistanceLineEdit->textEdited( value );
298  }
299 }
300 
301 
302 void QgsAdvancedDigitizingDockWidget::setCadEnabled( bool enabled )
303 {
304  mCadEnabled = enabled;
305  mEnableAction->setChecked( enabled );
306  mConstructionModeAction->setEnabled( enabled );
307  mParallelAction->setEnabled( enabled );
308  mPerpendicularAction->setEnabled( enabled );
309  mSettingsAction->setEnabled( enabled );
310  mInputWidgets->setEnabled( enabled );
311  mToggleFloaterAction->setEnabled( enabled );
312 
313  clear();
314  setConstructionMode( false );
315 
316  switchZM();
317  emit cadEnabledChanged( enabled );
318 }
319 
320 
322 {
323  bool enableZ = false;
324  bool enableM = false;
325 
326  if ( QgsMapLayer *layer = targetLayer() )
327  {
328  switch ( layer->type() )
329  {
331  {
332  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
333  const QgsWkbTypes::Type type = vlayer->wkbType();
334  enableZ = QgsWkbTypes::hasZ( type );
335  enableM = QgsWkbTypes::hasM( type );
336  break;
337  }
338 
340  {
341  QgsMeshLayer *mlayer = qobject_cast<QgsMeshLayer *>( layer );
342  enableZ = mlayer->isEditable();
343  break;
344  }
345 
352  break;
353  }
354  }
355 
356  setEnabledZ( enableZ );
357  setEnabledM( enableM );
358 }
359 
361 {
362  mRelativeZButton->setEnabled( enable );
363  mZLabel->setEnabled( enable );
364  mZLineEdit->setEnabled( enable );
365  if ( mZLineEdit->isEnabled() )
366  mZLineEdit->setText( QLocale().toString( QgsMapToolEdit( mMapCanvas ).defaultZValue(), 'f', 6 ) );
367  else
368  mZLineEdit->clear();
369  mLockZButton->setEnabled( enable );
370  emit enabledChangedZ( enable );
371 }
372 
374 {
375  mRelativeMButton->setEnabled( enable );
376  mMLabel->setEnabled( enable );
377  mMLineEdit->setEnabled( enable );
378  if ( mMLineEdit->isEnabled() )
379  mMLineEdit->setText( QLocale().toString( QgsMapToolEdit( mMapCanvas ).defaultMValue(), 'f', 6 ) );
380  else
381  mMLineEdit->clear();
382  mLockMButton->setEnabled( enable );
383  emit enabledChangedM( enable );
384 }
385 
386 void QgsAdvancedDigitizingDockWidget::activateCad( bool enabled )
387 {
388  enabled &= mCurrentMapToolSupportsCad;
389 
390  mSessionActive = enabled;
391 
392  if ( enabled && !isVisible() )
393  {
394  show();
395  }
396 
397  setCadEnabled( enabled );
398 }
399 
400 void QgsAdvancedDigitizingDockWidget::additionalConstraintClicked( bool activated )
401 {
402  if ( !activated )
403  {
404  lockAdditionalConstraint( AdditionalConstraint::NoConstraint );
405  }
406  else if ( sender() == mParallelAction )
407  {
408  lockAdditionalConstraint( AdditionalConstraint::Parallel );
409  }
410  else if ( sender() == mPerpendicularAction )
411  {
412  lockAdditionalConstraint( AdditionalConstraint::Perpendicular );
413  }
414 }
415 
416 void QgsAdvancedDigitizingDockWidget::setConstraintRelative( bool activate )
417 {
418  if ( sender() == mRelativeAngleButton )
419  {
420  mAngleConstraint->setRelative( activate );
421  emit relativeAngleChanged( activate );
422  }
423  else if ( sender() == mRelativeXButton )
424  {
425  mXConstraint->setRelative( activate );
426  emit relativeXChanged( activate );
427  }
428  else if ( sender() == mRelativeYButton )
429  {
430  mYConstraint->setRelative( activate );
431  emit relativeYChanged( activate );
432  }
433  else if ( sender() == mRelativeZButton )
434  {
435  mZConstraint->setRelative( activate );
436  emit relativeZChanged( activate );
437  }
438  else if ( sender() == mRelativeMButton )
439  {
440  mMConstraint->setRelative( activate );
441  emit relativeMChanged( activate );
442  }
443 }
444 
445 void QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock( bool activate )
446 {
447  if ( sender() == mRepeatingLockDistanceButton )
448  {
449  mDistanceConstraint->setRepeatingLock( activate );
450  }
451  else if ( sender() == mRepeatingLockAngleButton )
452  {
453  mAngleConstraint->setRepeatingLock( activate );
454  }
455  else if ( sender() == mRepeatingLockXButton )
456  {
457  mXConstraint->setRepeatingLock( activate );
458  }
459  else if ( sender() == mRepeatingLockYButton )
460  {
461  mYConstraint->setRepeatingLock( activate );
462  }
463  else if ( sender() == mRepeatingLockZButton )
464  {
465  mZConstraint->setRepeatingLock( activate );
466  }
467  else if ( sender() == mRepeatingLockMButton )
468  {
469  mMConstraint->setRepeatingLock( activate );
470  }
471 }
472 
473 void QgsAdvancedDigitizingDockWidget::setConstructionMode( bool enabled )
474 {
475  mConstructionMode = enabled;
476  mConstructionModeAction->setChecked( enabled );
477 }
478 
479 void QgsAdvancedDigitizingDockWidget::settingsButtonTriggered( QAction *action )
480 {
481  // common angles
482  const QMap<QAction *, double>::const_iterator ica = mCommonAngleActions.constFind( action );
483  if ( ica != mCommonAngleActions.constEnd() )
484  {
485  ica.key()->setChecked( true );
486  mCommonAngleConstraint = ica.value();
487  QgsSettings().setValue( QStringLiteral( "/Cad/CommonAngle" ), ica.value() );
488  mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
489  return;
490  }
491 }
492 
493 QgsMapLayer *QgsAdvancedDigitizingDockWidget::targetLayer()
494 {
495  if ( QgsMapToolAdvancedDigitizing *advancedTool = qobject_cast< QgsMapToolAdvancedDigitizing * >( mMapCanvas->mapTool() ) )
496  {
497  return advancedTool->layer();
498  }
499  else
500  {
501  return mMapCanvas->currentLayer();
502  }
503 }
504 
505 void QgsAdvancedDigitizingDockWidget::releaseLocks( bool releaseRepeatingLocks )
506 {
507  // release all locks except construction mode
508 
509  lockAdditionalConstraint( AdditionalConstraint::NoConstraint );
510 
511  if ( releaseRepeatingLocks || !mAngleConstraint->isRepeatingLock() )
512  {
513  mAngleConstraint->setLockMode( CadConstraint::NoLock );
514  emit lockAngleChanged( false );
515  }
516  if ( releaseRepeatingLocks || !mDistanceConstraint->isRepeatingLock() )
517  {
518  mDistanceConstraint->setLockMode( CadConstraint::NoLock );
519  emit lockDistanceChanged( false );
520  }
521  if ( releaseRepeatingLocks || !mXConstraint->isRepeatingLock() )
522  {
523  mXConstraint->setLockMode( CadConstraint::NoLock );
524  emit lockXChanged( false );
525  }
526  if ( releaseRepeatingLocks || !mYConstraint->isRepeatingLock() )
527  {
528  mYConstraint->setLockMode( CadConstraint::NoLock );
529  emit lockYChanged( false );
530  }
531  if ( releaseRepeatingLocks || !mZConstraint->isRepeatingLock() )
532  {
533  mZConstraint->setLockMode( CadConstraint::NoLock );
534  emit lockZChanged( false );
535  }
536  if ( releaseRepeatingLocks || !mMConstraint->isRepeatingLock() )
537  {
538  mMConstraint->setLockMode( CadConstraint::NoLock );
539  emit lockMChanged( false );
540  }
541 
542  if ( !mCadPointList.empty() )
543  {
544  if ( !mXConstraint->isLocked() && !mXConstraint->relative() )
545  {
546  mXConstraint->setValue( mCadPointList.constLast().x(), true );
547  }
548  if ( !mYConstraint->isLocked() && !mYConstraint->relative() )
549  {
550  mYConstraint->setValue( mCadPointList.constLast().y(), true );
551  }
552  if ( !mZConstraint->isLocked() && !mZConstraint->relative() )
553  {
554  mZConstraint->setValue( mCadPointList.constLast().z(), true );
555  }
556  if ( !mMConstraint->isLocked() && !mMConstraint->relative() )
557  {
558  mMConstraint->setValue( mCadPointList.constLast().m(), true );
559  }
560  }
561 
562 }
563 
564 #if 0
565 void QgsAdvancedDigitizingDockWidget::emit pointChanged()
566 {
567  // run a fake map mouse event to update the paint item
568  QPoint globalPos = mMapCanvas->cursor().pos();
569  QPoint pos = mMapCanvas->mapFromGlobal( globalPos );
570  QMouseEvent *e = new QMouseEvent( QEvent::MouseMove, pos, globalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier );
571  mCurrentMapTool->canvasMoveEvent( e );
572 }
573 #endif
574 
575 QgsAdvancedDigitizingDockWidget::CadConstraint *QgsAdvancedDigitizingDockWidget::objectToConstraint( const QObject *obj ) const
576 {
577  CadConstraint *constraint = nullptr;
578  if ( obj == mAngleLineEdit || obj == mLockAngleButton )
579  {
580  constraint = mAngleConstraint.get();
581  }
582  else if ( obj == mDistanceLineEdit || obj == mLockDistanceButton )
583  {
584  constraint = mDistanceConstraint.get();
585  }
586  else if ( obj == mXLineEdit || obj == mLockXButton )
587  {
588  constraint = mXConstraint.get();
589  }
590  else if ( obj == mYLineEdit || obj == mLockYButton )
591  {
592  constraint = mYConstraint.get();
593  }
594  else if ( obj == mZLineEdit || obj == mLockZButton )
595  {
596  constraint = mZConstraint.get();
597  }
598  else if ( obj == mMLineEdit || obj == mLockMButton )
599  {
600  constraint = mMConstraint.get();
601  }
602  return constraint;
603 }
604 
605 double QgsAdvancedDigitizingDockWidget::parseUserInput( const QString &inputValue, bool &ok ) const
606 {
607  ok = false;
608  double value = qgsPermissiveToDouble( inputValue, ok );
609  if ( ok )
610  {
611  return value;
612  }
613  else
614  {
615  // try to evaluate expression
616  QgsExpression expr( inputValue );
617  const QVariant result = expr.evaluate();
618  if ( expr.hasEvalError() )
619  ok = false;
620  else
621  value = result.toDouble( &ok );
622  return value;
623  }
624 }
625 
626 void QgsAdvancedDigitizingDockWidget::updateConstraintValue( CadConstraint *constraint, const QString &textValue, bool convertExpression )
627 {
628  if ( !constraint || textValue.isEmpty() )
629  {
630  return;
631  }
632 
633  if ( constraint->lockMode() == CadConstraint::NoLock )
634  return;
635 
636  bool ok;
637  const double value = parseUserInput( textValue, ok );
638  if ( !ok )
639  return;
640 
641  constraint->setValue( value, convertExpression );
642  // run a fake map mouse event to update the paint item
643  emit pointChangedV2( mCadPointList.value( 0 ) );
644 }
645 
646 void QgsAdvancedDigitizingDockWidget::lockConstraint( bool activate /* default true */ )
647 {
648  CadConstraint *constraint = objectToConstraint( sender() );
649  if ( !constraint )
650  {
651  return;
652  }
653 
654  if ( activate )
655  {
656  const QString textValue = constraint->lineEdit()->text();
657  if ( !textValue.isEmpty() )
658  {
659  bool ok;
660  const double value = parseUserInput( textValue, ok );
661  if ( ok )
662  {
663  constraint->setValue( value );
664  }
665  else
666  {
667  activate = false;
668  }
669  }
670  else
671  {
672  activate = false;
673  }
674  }
675  constraint->setLockMode( activate ? CadConstraint::HardLock : CadConstraint::NoLock );
676 
677  if ( constraint == mXConstraint.get() )
678  {
679  emit lockXChanged( activate );
680  }
681  else if ( constraint == mYConstraint.get() )
682  {
683  emit lockYChanged( activate );
684  }
685  else if ( constraint == mZConstraint.get() )
686  {
687  emit lockZChanged( activate );
688  }
689  else if ( constraint == mMConstraint.get() )
690  {
691  emit lockMChanged( activate );
692  }
693  else if ( constraint == mDistanceConstraint.get() )
694  {
695  emit lockDistanceChanged( activate );
696  }
697  else if ( constraint == mAngleConstraint.get() )
698  {
699  emit lockAngleChanged( activate );
700  }
701 
702  if ( activate )
703  {
704  // deactivate perpendicular/parallel if angle has been activated
705  if ( constraint == mAngleConstraint.get() )
706  {
707  lockAdditionalConstraint( AdditionalConstraint::NoConstraint );
708  }
709 
710  // run a fake map mouse event to update the paint item
711  emit pointChangedV2( mCadPointList.value( 0 ) );
712  }
713 }
714 
715 void QgsAdvancedDigitizingDockWidget::constraintTextEdited( const QString &textValue )
716 {
717  CadConstraint *constraint = objectToConstraint( sender() );
718  if ( !constraint )
719  {
720  return;
721  }
722 
723  updateConstraintValue( constraint, textValue, false );
724 }
725 
726 void QgsAdvancedDigitizingDockWidget::constraintFocusOut()
727 {
728  QLineEdit *lineEdit = qobject_cast< QLineEdit * >( sender()->parent() );
729  if ( !lineEdit )
730  return;
731 
732  CadConstraint *constraint = objectToConstraint( lineEdit );
733  if ( !constraint )
734  {
735  return;
736  }
737 
738  updateConstraintValue( constraint, lineEdit->text(), true );
739 }
740 
741 void QgsAdvancedDigitizingDockWidget::lockAdditionalConstraint( AdditionalConstraint constraint )
742 {
743  mAdditionalConstraint = constraint;
744  mPerpendicularAction->setChecked( constraint == AdditionalConstraint::Perpendicular );
745  mParallelAction->setChecked( constraint == AdditionalConstraint::Parallel );
746 }
747 
748 void QgsAdvancedDigitizingDockWidget::updateCapacity( bool updateUIwithoutChange )
749 {
750  CadCapacities newCapacities = CadCapacities();
751  const bool isGeographic = mMapCanvas->mapSettings().destinationCrs().isGeographic();
752  if ( !isGeographic )
753  newCapacities |= Distance;
754 
755  // first point is the mouse point (it doesn't count)
756  if ( mCadPointList.count() > 1 )
757  {
758  newCapacities |= RelativeCoordinates;
759  if ( !isGeographic )
760  newCapacities |= AbsoluteAngle;
761  }
762  if ( mCadPointList.count() > 2 )
763  {
764  if ( !isGeographic )
765  newCapacities |= RelativeAngle;
766  }
767  if ( !updateUIwithoutChange && newCapacities == mCapacities )
768  {
769  return;
770  }
771 
772  const bool snappingEnabled = QgsProject::instance()->snappingConfig().enabled();
773 
774  // update the UI according to new capacities
775  // still keep the old to compare
776 
777  const bool distance = mCadEnabled && newCapacities.testFlag( Distance );
778  const bool relativeAngle = mCadEnabled && newCapacities.testFlag( RelativeAngle );
779  const bool absoluteAngle = mCadEnabled && newCapacities.testFlag( AbsoluteAngle );
780  const bool relativeCoordinates = mCadEnabled && newCapacities.testFlag( RelativeCoordinates );
781 
782  mPerpendicularAction->setEnabled( distance && absoluteAngle && snappingEnabled );
783  mParallelAction->setEnabled( distance && absoluteAngle && snappingEnabled );
784 
785  //update tooltips on buttons
786  if ( !snappingEnabled )
787  {
788  mPerpendicularAction->setToolTip( tr( "Snapping must be enabled to utilize perpendicular mode" ) );
789  mParallelAction->setToolTip( tr( "Snapping must be enabled to utilize parallel mode" ) );
790  }
791  else
792  {
793  mPerpendicularAction->setToolTip( "<b>" + tr( "Perpendicular" ) + "</b><br>(" + tr( "press p to switch between perpendicular, parallel and normal mode" ) + ")" );
794  mParallelAction->setToolTip( "<b>" + tr( "Parallel" ) + "</b><br>(" + tr( "press p to switch between perpendicular, parallel and normal mode" ) + ")" );
795  }
796 
797 
798  if ( !absoluteAngle )
799  {
800  lockAdditionalConstraint( AdditionalConstraint::NoConstraint );
801  }
802 
803  // absolute angle = azimuth, relative = from previous line
804  mLockAngleButton->setEnabled( absoluteAngle );
805  mRelativeAngleButton->setEnabled( relativeAngle );
806  mAngleLineEdit->setEnabled( absoluteAngle );
807  emit enabledChangedAngle( absoluteAngle );
808  if ( !absoluteAngle )
809  {
810  mAngleConstraint->setLockMode( CadConstraint::NoLock );
811  }
812  if ( !relativeAngle )
813  {
814  mAngleConstraint->setRelative( false );
815  emit relativeAngleChanged( false );
816  }
817  else if ( relativeAngle && !mCapacities.testFlag( RelativeAngle ) )
818  {
819  // set angle mode to relative if can do and wasn't available before
820  mAngleConstraint->setRelative( true );
821  emit relativeAngleChanged( true );
822  }
823 
824  // distance is always relative
825  mLockDistanceButton->setEnabled( distance && relativeCoordinates );
826  mDistanceLineEdit->setEnabled( distance && relativeCoordinates );
827  emit enabledChangedDistance( distance && relativeCoordinates );
828  if ( !( distance && relativeCoordinates ) )
829  {
830  mDistanceConstraint->setLockMode( CadConstraint::NoLock );
831  }
832 
833  mRelativeXButton->setEnabled( relativeCoordinates );
834  mRelativeYButton->setEnabled( relativeCoordinates );
835  mRelativeZButton->setEnabled( relativeCoordinates );
836  mRelativeMButton->setEnabled( relativeCoordinates );
837 
838  // update capacities
839  mCapacities = newCapacities;
840  mCadPaintItem->updatePosition();
841 }
842 
843 
845 {
848  constr.relative = c->relative();
849  constr.value = c->value();
850  return constr;
851 }
852 
854 {
856  context.snappingUtils = mMapCanvas->snappingUtils();
857  context.mapUnitsPerPixel = mMapCanvas->mapUnitsPerPixel();
858  context.xConstraint = _constraint( mXConstraint.get() );
859  context.yConstraint = _constraint( mYConstraint.get() );
860  context.zConstraint = _constraint( mZConstraint.get() );
861  context.mConstraint = _constraint( mMConstraint.get() );
862  context.distanceConstraint = _constraint( mDistanceConstraint.get() );
863  context.angleConstraint = _constraint( mAngleConstraint.get() );
864  context.setCadPoints( mCadPointList );
865 
868  context.commonAngleConstraint.value = mCommonAngleConstraint;
869 
871 
872  const bool res = output.valid;
873  QgsPoint point = pointXYToPoint( output.finalMapPoint );
874  mSnappedSegment.clear();
875  if ( output.snapMatch.hasEdge() )
876  {
877  QgsPointXY edgePt0, edgePt1;
878  output.snapMatch.edgePoints( edgePt0, edgePt1 );
879  mSnappedSegment << edgePt0 << edgePt1;
880  }
881  if ( mAngleConstraint->lockMode() != CadConstraint::HardLock )
882  {
883  if ( output.softLockCommonAngle != -1 )
884  {
885  mAngleConstraint->setLockMode( CadConstraint::SoftLock );
886  mAngleConstraint->setValue( output.softLockCommonAngle );
887  }
888  else
889  {
890  mAngleConstraint->setLockMode( CadConstraint::NoLock );
891  }
892  }
893 
894  if ( output.snapMatch.isValid() )
895  {
896  mSnapIndicator->setMatch( output.snapMatch );
897  mSnapIndicator->setVisible( true );
898  }
899  else
900  {
901  mSnapIndicator->setVisible( false );
902  }
903 
904  /*
905  * Ensure that Z and M are passed
906  * It will be dropped as needed later.
907  */
908  point.setZ( QgsMapToolEdit( mMapCanvas ).defaultZValue() );
909  point.setM( QgsMapToolEdit( mMapCanvas ).defaultMValue() );
910 
911  /*
912  * Constraints are applied in 2D, they are always called when using the tool
913  * but they do not take into account if when you snap on a vertex it has
914  * a Z value.
915  * To get the value we use the snapPoint method. However, we only apply it
916  * when the snapped point corresponds to the constrained point or on an edge
917  * if the topological editing is activated.
918  */
919  e->setMapPoint( point );
920  mSnapMatch = context.snappingUtils->snapToMap( point, nullptr, true );
921  if ( ( ( mSnapMatch.hasVertex() || mSnapMatch.hasLineEndpoint() ) && ( point == mSnapMatch.point() ) ) || ( mSnapMatch.hasEdge() && QgsProject::instance()->topologicalEditing() ) )
922  {
923  e->snapPoint();
924  point = mSnapMatch.interpolatedPoint();
925  }
926 
927  /*
928  * And if M or Z lock button is activated get the value of the input.
929  */
930  if ( mLockZButton->isChecked() )
931  {
932  point.setZ( QLocale().toDouble( mZLineEdit->text() ) );
933  }
934  if ( mLockMButton->isChecked() )
935  {
936  point.setM( QLocale().toDouble( mMLineEdit->text() ) );
937  }
938 
939  // update the point list
940  updateCurrentPoint( point );
941 
942  updateUnlockedConstraintValues( point );
943 
944  if ( res )
945  {
946  emit popWarning();
947  }
948  else
949  {
950  emit pushWarning( tr( "Some constraints are incompatible. Resulting point might be incorrect." ) );
951  }
952 
953  return res;
954 }
955 
956 
957 void QgsAdvancedDigitizingDockWidget::updateUnlockedConstraintValues( const QgsPoint &point )
958 {
959  bool previousPointExist, penulPointExist;
960  const QgsPoint previousPt = previousPointV2( &previousPointExist );
961  const QgsPoint penultimatePt = penultimatePointV2( &penulPointExist );
962 
963  // --- angle
964  if ( !mAngleConstraint->isLocked() && previousPointExist )
965  {
966  double angle = 0.0;
967  if ( penulPointExist && mAngleConstraint->relative() )
968  {
969  // previous angle
970  angle = std::atan2( previousPt.y() - penultimatePt.y(),
971  previousPt.x() - penultimatePt.x() );
972  }
973  angle = ( std::atan2( point.y() - previousPt.y(),
974  point.x() - previousPt.x()
975  ) - angle ) * 180 / M_PI;
976  // modulus
977  angle = std::fmod( angle, 360.0 );
978  mAngleConstraint->setValue( angle );
979  }
980  // --- distance
981  if ( !mDistanceConstraint->isLocked() && previousPointExist )
982  {
983  mDistanceConstraint->setValue( std::sqrt( previousPt.distanceSquared( point ) ) );
984  }
985  // --- X
986  if ( !mXConstraint->isLocked() )
987  {
988  if ( previousPointExist && mXConstraint->relative() )
989  {
990  mXConstraint->setValue( point.x() - previousPt.x() );
991  }
992  else
993  {
994  mXConstraint->setValue( point.x() );
995  }
996  }
997  // --- Y
998  if ( !mYConstraint->isLocked() )
999  {
1000  if ( previousPointExist && mYConstraint->relative() )
1001  {
1002  mYConstraint->setValue( point.y() - previousPt.y() );
1003  }
1004  else
1005  {
1006  mYConstraint->setValue( point.y() );
1007  }
1008  }
1009  // --- Z
1010  if ( !mZConstraint->isLocked() )
1011  {
1012  if ( previousPointExist && mZConstraint->relative() )
1013  {
1014  mZConstraint->setValue( point.z() - previousPt.z() );
1015  }
1016  else
1017  {
1018  mZConstraint->setValue( point.z() );
1019  }
1020  }
1021  // --- M
1022  if ( !mMConstraint->isLocked() )
1023  {
1024  if ( previousPointExist && mMConstraint->relative() )
1025  {
1026  mMConstraint->setValue( point.m() - previousPt.m() );
1027  }
1028  else
1029  {
1030  mMConstraint->setValue( point.m() );
1031  }
1032  }
1033 }
1034 
1035 
1036 QList<QgsPointXY> QgsAdvancedDigitizingDockWidget::snapSegmentToAllLayers( const QgsPointXY &originalMapPoint, bool *snapped ) const
1037 {
1038  QList<QgsPointXY> segment;
1039  QgsPointXY pt1, pt2;
1040  QgsPointLocator::Match match;
1041 
1042  QgsSnappingUtils *snappingUtils = mMapCanvas->snappingUtils();
1043 
1044  const QgsSnappingConfig canvasConfig = snappingUtils->config();
1045  QgsSnappingConfig localConfig = snappingUtils->config();
1046 
1047  localConfig.setMode( QgsSnappingConfig::AllLayers );
1049  snappingUtils->setConfig( localConfig );
1050 
1051  match = snappingUtils->snapToMap( originalMapPoint, nullptr, true );
1052 
1053  snappingUtils->setConfig( canvasConfig );
1054 
1055  if ( match.isValid() && match.hasEdge() )
1056  {
1057  match.edgePoints( pt1, pt2 );
1058  segment << pt1 << pt2;
1059  }
1060 
1061  if ( snapped )
1062  {
1063  *snapped = segment.count() == 2;
1064  }
1065 
1066  return segment;
1067 }
1068 
1070 {
1071  if ( mAdditionalConstraint == AdditionalConstraint::NoConstraint )
1072  {
1073  return false;
1074  }
1075 
1076  bool previousPointExist, penulPointExist, snappedSegmentExist;
1077  const QgsPoint previousPt = previousPointV2( &previousPointExist );
1078  const QgsPoint penultimatePt = penultimatePointV2( &penulPointExist );
1079  mSnappedSegment = snapSegmentToAllLayers( e->originalMapPoint(), &snappedSegmentExist );
1080 
1081  if ( !previousPointExist || !snappedSegmentExist )
1082  {
1083  return false;
1084  }
1085 
1086  double angle = std::atan2( mSnappedSegment[0].y() - mSnappedSegment[1].y(), mSnappedSegment[0].x() - mSnappedSegment[1].x() );
1087 
1088  if ( mAngleConstraint->relative() && penulPointExist )
1089  {
1090  angle -= std::atan2( previousPt.y() - penultimatePt.y(), previousPt.x() - penultimatePt.x() );
1091  }
1092 
1093  if ( mAdditionalConstraint == AdditionalConstraint::Perpendicular )
1094  {
1095  angle += M_PI_2;
1096  }
1097 
1098  angle *= 180 / M_PI;
1099 
1100  mAngleConstraint->setValue( angle );
1101  mAngleConstraint->setLockMode( lockMode );
1102  if ( lockMode == CadConstraint::HardLock )
1103  {
1104  mAdditionalConstraint = AdditionalConstraint::NoConstraint;
1105  }
1106 
1107  return true;
1108 }
1109 
1111 {
1112  // event on map tool
1113 
1114  if ( !mCadEnabled )
1115  return false;
1116 
1117  switch ( e->key() )
1118  {
1119  case Qt::Key_Backspace:
1120  case Qt::Key_Delete:
1121  {
1123  releaseLocks( false );
1124  break;
1125  }
1126  case Qt::Key_Escape:
1127  {
1128  releaseLocks();
1129  break;
1130  }
1131  default:
1132  {
1133  keyPressEvent( e );
1134  break;
1135  }
1136  }
1137  // for map tools, continues with key press in any case
1138  return false;
1139 }
1140 
1142 {
1143  clearPoints();
1144  releaseLocks();
1145 }
1146 
1148 {
1149  // event on dock (this)
1150 
1151  if ( !mCadEnabled )
1152  return;
1153 
1154  switch ( e->key() )
1155  {
1156  case Qt::Key_Backspace:
1157  case Qt::Key_Delete:
1158  {
1160  releaseLocks( false );
1161  break;
1162  }
1163  case Qt::Key_Escape:
1164  {
1165  releaseLocks();
1166  break;
1167  }
1168  default:
1169  {
1170  filterKeyPress( e );
1171  break;
1172  }
1173  }
1174 }
1175 
1176 void QgsAdvancedDigitizingDockWidget::setPoints( const QList<QgsPointXY> &points )
1177 {
1178  clearPoints();
1179  const auto constPoints = points;
1180  for ( const QgsPointXY &pt : constPoints )
1181  {
1182  addPoint( pt );
1183  }
1184 }
1185 
1186 bool QgsAdvancedDigitizingDockWidget::eventFilter( QObject *obj, QEvent *event )
1187 {
1188  if ( !cadEnabled() )
1189  {
1190  return QgsDockWidget::eventFilter( obj, event );
1191  }
1192 
1193  // event for line edits and map canvas
1194  // we have to catch both KeyPress events and ShortcutOverride events. This is because
1195  // the Ctrl+D and Ctrl+A shortcuts for locking distance/angle clash with the global
1196  // "remove layer" and "select all" shortcuts. Catching ShortcutOverride events allows
1197  // us to intercept these keystrokes before they are caught by the global shortcuts
1198  if ( event->type() == QEvent::ShortcutOverride || event->type() == QEvent::KeyPress )
1199  {
1200  if ( QKeyEvent *keyEvent = dynamic_cast<QKeyEvent *>( event ) )
1201  {
1202  return filterKeyPress( keyEvent );
1203  }
1204  }
1205  return QgsDockWidget::eventFilter( obj, event );
1206 }
1207 
1208 bool QgsAdvancedDigitizingDockWidget::filterKeyPress( QKeyEvent *e )
1209 {
1210  // we need to be careful here -- because this method is called on both KeyPress events AND
1211  // ShortcutOverride events, we have to take care that we don't trigger the handling for BOTH
1212  // these event types for a single key press. I.e. pressing "A" may first call trigger a
1213  // ShortcutOverride event (sometimes, not always!) followed immediately by a KeyPress event.
1214  const QEvent::Type type = e->type();
1215  switch ( e->key() )
1216  {
1217  case Qt::Key_X:
1218  {
1219  // modifier+x ONLY caught for ShortcutOverride events...
1220  if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1221  {
1222  mXConstraint->toggleLocked();
1223  emit lockXChanged( mXConstraint->isLocked() );
1224  emit pointChangedV2( mCadPointList.value( 0 ) );
1225  e->accept();
1226  }
1227  else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1228  {
1229  if ( mCapacities.testFlag( RelativeCoordinates ) )
1230  {
1231  mXConstraint->toggleRelative();
1232  emit relativeXChanged( mXConstraint->relative() );
1233  emit pointChangedV2( mCadPointList.value( 0 ) );
1234  e->accept();
1235  }
1236  }
1237  // .. but "X" alone ONLY caught for KeyPress events (see comment at start of function)
1238  else if ( type == QEvent::KeyPress )
1239  {
1240  mXLineEdit->setFocus();
1241  mXLineEdit->selectAll();
1242  emit focusOnXRequested();
1243  e->accept();
1244  }
1245  break;
1246  }
1247  case Qt::Key_Y:
1248  {
1249  // modifier+y ONLY caught for ShortcutOverride events...
1250  if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1251  {
1252  mYConstraint->toggleLocked();
1253  emit lockYChanged( mYConstraint->isLocked() );
1254  emit pointChangedV2( mCadPointList.value( 0 ) );
1255  e->accept();
1256  }
1257  else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1258  {
1259  if ( mCapacities.testFlag( RelativeCoordinates ) )
1260  {
1261  mYConstraint->toggleRelative();
1262  emit relativeYChanged( mYConstraint->relative() );
1263  emit pointChangedV2( mCadPointList.value( 0 ) );
1264  e->accept();
1265  }
1266  }
1267  // .. but "y" alone ONLY caught for KeyPress events (see comment at start of function)
1268  else if ( type == QEvent::KeyPress )
1269  {
1270  mYLineEdit->setFocus();
1271  mYLineEdit->selectAll();
1272  emit focusOnYRequested();
1273  e->accept();
1274  }
1275  break;
1276  }
1277  case Qt::Key_Z:
1278  {
1279  // modifier+z ONLY caught for ShortcutOverride events...
1280  if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::AltModifier )
1281  {
1282  mZConstraint->toggleLocked();
1283  emit lockZChanged( mZConstraint->isLocked() );
1284  emit pointChangedV2( mCadPointList.value( 0 ) );
1285  e->accept();
1286  }
1287  else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1288  {
1289  if ( mCapacities.testFlag( RelativeCoordinates ) )
1290  {
1291  mZConstraint->toggleRelative();
1292  emit relativeZChanged( mZConstraint->relative() );
1293  emit pointChangedV2( mCadPointList.value( 0 ) );
1294  e->accept();
1295  }
1296  }
1297  // .. but "z" alone ONLY caught for KeyPress events (see comment at start of function)
1298  else if ( type == QEvent::KeyPress )
1299  {
1300  mZLineEdit->setFocus();
1301  mZLineEdit->selectAll();
1302  emit focusOnZRequested();
1303  e->accept();
1304  }
1305  break;
1306  }
1307  case Qt::Key_M:
1308  {
1309  // modifier+m ONLY caught for ShortcutOverride events...
1310  if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1311  {
1312  mMConstraint->toggleLocked();
1313  emit lockMChanged( mMConstraint->isLocked() );
1314  emit pointChangedV2( mCadPointList.value( 0 ) );
1315  e->accept();
1316  }
1317  else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1318  {
1319  if ( mCapacities.testFlag( RelativeCoordinates ) )
1320  {
1321  mMConstraint->toggleRelative();
1322  emit relativeMChanged( mMConstraint->relative() );
1323  emit pointChangedV2( mCadPointList.value( 0 ) );
1324  e->accept();
1325  }
1326  }
1327  // .. but "m" alone ONLY caught for KeyPress events (see comment at start of function)
1328  else if ( type == QEvent::KeyPress )
1329  {
1330  mMLineEdit->setFocus();
1331  mMLineEdit->selectAll();
1332  emit focusOnMRequested();
1333  e->accept();
1334  }
1335  break;
1336  }
1337  case Qt::Key_A:
1338  {
1339  // modifier+a ONLY caught for ShortcutOverride events...
1340  if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1341  {
1342  if ( mCapacities.testFlag( AbsoluteAngle ) )
1343  {
1344  mAngleConstraint->toggleLocked();
1345  emit lockAngleChanged( mAngleConstraint->isLocked() );
1346  emit pointChangedV2( mCadPointList.value( 0 ) );
1347  e->accept();
1348  }
1349  }
1350  else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1351  {
1352  if ( mCapacities.testFlag( RelativeAngle ) )
1353  {
1354  mAngleConstraint->toggleRelative();
1355  emit relativeAngleChanged( mAngleConstraint->relative() );
1356  emit pointChangedV2( mCadPointList.value( 0 ) );
1357  e->accept();
1358  }
1359  }
1360  // .. but "a" alone ONLY caught for KeyPress events (see comment at start of function)
1361  else if ( type == QEvent::KeyPress )
1362  {
1363  mAngleLineEdit->setFocus();
1364  mAngleLineEdit->selectAll();
1365  emit focusOnAngleRequested();
1366  e->accept();
1367  }
1368  break;
1369  }
1370  case Qt::Key_D:
1371  {
1372  // modifier+d ONLY caught for ShortcutOverride events...
1373  if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1374  {
1375  if ( mCapacities.testFlag( RelativeCoordinates ) && mCapacities.testFlag( Distance ) )
1376  {
1377  mDistanceConstraint->toggleLocked();
1378  emit lockDistanceChanged( mDistanceConstraint->isLocked() );
1379  emit pointChangedV2( mCadPointList.value( 0 ) );
1380  e->accept();
1381  }
1382  }
1383  // .. but "d" alone ONLY caught for KeyPress events (see comment at start of function)
1384  else if ( type == QEvent::KeyPress )
1385  {
1386  mDistanceLineEdit->setFocus();
1387  mDistanceLineEdit->selectAll();
1388  emit focusOnDistanceRequested();
1389  e->accept();
1390  }
1391  break;
1392  }
1393  case Qt::Key_C:
1394  {
1395  if ( type == QEvent::KeyPress )
1396  {
1397  setConstructionMode( !mConstructionMode );
1398  e->accept();
1399  }
1400  break;
1401  }
1402  case Qt::Key_P:
1403  {
1404  if ( type == QEvent::KeyPress )
1405  {
1406  const bool parallel = mParallelAction->isChecked();
1407  const bool perpendicular = mPerpendicularAction->isChecked();
1408 
1409  if ( !parallel && !perpendicular )
1410  {
1411  lockAdditionalConstraint( AdditionalConstraint::Perpendicular );
1412  }
1413  else if ( perpendicular )
1414  {
1415  lockAdditionalConstraint( AdditionalConstraint::Parallel );
1416  }
1417  else
1418  {
1419  lockAdditionalConstraint( AdditionalConstraint::NoConstraint );
1420  }
1421  e->accept();
1422 
1423  // run a fake map mouse event to update the paint item
1424  emit pointChangedV2( mCadPointList.value( 0 ) );
1425  }
1426  break;
1427  }
1428  default:
1429  {
1430  return false; // continues
1431  }
1432  }
1433  return e->isAccepted();
1434 }
1435 
1437 {
1438  connect( mMapCanvas, &QgsMapCanvas::destinationCrsChanged, this, &QgsAdvancedDigitizingDockWidget::enable, Qt::UniqueConnection );
1439  if ( mMapCanvas->mapSettings().destinationCrs().isGeographic() )
1440  {
1441  mAngleLineEdit->setEnabled( false );
1442  mAngleLineEdit->setToolTip( tr( "Angle constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1443 
1444  mDistanceLineEdit->setEnabled( false );
1445  mDistanceLineEdit->setToolTip( tr( "Distance constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1446 
1447  mLabelX->setText( tr( "Long" ) );
1448  mLabelY->setText( tr( "Lat" ) );
1449 
1450  mXConstraint->setPrecision( 8 );
1451  mYConstraint->setPrecision( 8 );
1452  }
1453  else
1454  {
1455  mAngleLineEdit->setToolTip( "<b>" + tr( "Angle" ) + "</b><br>(" + tr( "press a for quick access" ) + ")" );
1456  mAngleLineEdit->setToolTip( QString() );
1457 
1458  mDistanceLineEdit->setEnabled( true );
1459  mDistanceLineEdit->setToolTip( "<b>" + tr( "Distance" ) + "</b><br>(" + tr( "press d for quick access" ) + ")" );
1460 
1461  mLabelX->setText( tr( "x" ) );
1462  mLabelY->setText( tr( "y" ) );
1463 
1464  mXConstraint->setPrecision( 6 );
1465  mYConstraint->setPrecision( 6 );
1466  }
1467 
1468  mEnableAction->setEnabled( true );
1469  mErrorLabel->hide();
1470  mCadWidget->show();
1471 
1472  mCurrentMapToolSupportsCad = true;
1473 
1474  if ( mSessionActive && !isVisible() )
1475  {
1476  show();
1477  }
1478  setCadEnabled( mSessionActive );
1479 }
1480 
1482 {
1484 
1485  mEnableAction->setEnabled( false );
1486  mErrorLabel->setText( tr( "CAD tools are not enabled for the current map tool" ) );
1487  mErrorLabel->show();
1488  mCadWidget->hide();
1489 
1490  mCurrentMapToolSupportsCad = false;
1491 
1492  setCadEnabled( false );
1493 }
1494 
1496 {
1497  mCadPaintItem->update();
1498 }
1499 
1501 {
1502  QgsPoint pt = pointXYToPoint( point );
1503  if ( !pointsCount() )
1504  {
1505  mCadPointList << pt;
1506  }
1507  else
1508  {
1509  mCadPointList.insert( 0, pt );
1510  }
1511 
1512  updateCapacity();
1514 }
1515 
1517 {
1518  if ( !pointsCount() )
1519  return;
1520 
1521  const int i = pointsCount() > 1 ? 1 : 0;
1522  mCadPointList.removeAt( i );
1523  updateCapacity();
1525 }
1526 
1528 {
1529  mCadPointList.clear();
1530  mSnappedSegment.clear();
1531 
1532  updateCapacity();
1534 }
1535 
1536 void QgsAdvancedDigitizingDockWidget::updateCurrentPoint( const QgsPoint &point )
1537 {
1538  if ( !pointsCount() )
1539  {
1540  mCadPointList << point;
1541  updateCapacity();
1542  }
1543  else
1544  {
1545  mCadPointList[0] = point;
1546  }
1548 }
1549 
1550 
1552 {
1553  mLockMode = mode;
1554  mLockerButton->setChecked( mode == HardLock );
1555  if ( mRepeatingLockButton )
1556  {
1557  if ( mode == HardLock )
1558  {
1559  mRepeatingLockButton->setEnabled( true );
1560  }
1561  else
1562  {
1563  mRepeatingLockButton->setChecked( false );
1564  mRepeatingLockButton->setEnabled( false );
1565  }
1566  }
1567 
1568  if ( mode == NoLock )
1569  {
1570  mLineEdit->clear();
1571  }
1572 
1573 }
1574 
1576 {
1577  mRepeatingLock = repeating;
1578  if ( mRepeatingLockButton )
1579  mRepeatingLockButton->setChecked( repeating );
1580 }
1581 
1583 {
1584  mRelative = relative;
1585  if ( mRelativeButton )
1586  {
1587  mRelativeButton->setChecked( relative );
1588  }
1589 }
1590 
1591 void QgsAdvancedDigitizingDockWidget::CadConstraint::setValue( double value, bool updateWidget )
1592 {
1593  mValue = value;
1594  if ( updateWidget && mLineEdit->isEnabled() )
1595  mLineEdit->setText( QLocale().toString( value, 'f', mPrecision ) );
1596 }
1597 
1599 {
1600  setLockMode( mLockMode == HardLock ? NoLock : HardLock );
1601 }
1602 
1604 {
1605  setRelative( !mRelative );
1606 }
1607 
1609 {
1610  mPrecision = precision;
1611  if ( mLineEdit->isEnabled() )
1612  mLineEdit->setText( QLocale().toString( mValue, 'f', mPrecision ) );
1613 }
1614 
1616 {
1617  if ( exist )
1618  *exist = pointsCount() > 0;
1619  if ( pointsCount() > 0 )
1620  return mCadPointList.value( 0 );
1621  else
1622  return QgsPoint();
1623 }
1624 
1626 {
1627  if ( pointsCount() > 0 && layer )
1628  {
1629  QgsPoint res = mCadPointList.value( 0 );
1630  const QgsPointXY layerCoordinates = mMapCanvas->mapSettings().mapToLayerCoordinates( layer, res );
1631  res.setX( layerCoordinates.x() );
1632  res.setY( layerCoordinates.y() );
1633  return res;
1634  }
1635  return QgsPoint();
1636 }
1637 
1639 {
1640  if ( exist )
1641  *exist = pointsCount() > 1;
1642  if ( pointsCount() > 1 )
1643  return mCadPointList.value( 1 );
1644  else
1645  return QgsPoint();
1646 }
1647 
1649 {
1650  if ( exist )
1651  *exist = pointsCount() > 2;
1652  if ( pointsCount() > 2 )
1653  return mCadPointList.value( 2 );
1654  else
1655  return QgsPoint();
1656 }
1657 
1658 QgsPoint QgsAdvancedDigitizingDockWidget::pointXYToPoint( const QgsPointXY &point ) const
1659 {
1660  return QgsPoint( point.x(), point.y(), getLineZ(), getLineM() );
1661 }
1662 
1664 {
1665  return mZLineEdit->isEnabled() ? QLocale().toDouble( mZLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
1666 }
1667 
1669 {
1670  return mMLineEdit->isEnabled() ? QLocale().toDouble( mMLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
1671 }
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 CadConstraint is an abstract class for all basic constraints (angle/distance/x/y).
void setPrecision(int precision)
Sets the numeric precision (decimal places) to show in the associated widget.
void setRepeatingLock(bool repeating)
Sets whether a repeating lock is set for the constraint.
void setRelative(bool relative)
Set if the constraint should be treated relative.
void setValue(double value, bool updateWidget=true)
Set the value of the constraint.
void valueDistanceChanged(const QString &value)
Emitted whenever the distance value changes (either the mouse moved, or the user changed the input).
void lockZChanged(bool locked)
Emitted whenever the Z parameter is locked.
void setEnabledZ(bool enable)
Sets whether Z is enabled.
void setPoints(const QList< QgsPointXY > &points)
Configures list of current CAD points.
void setZ(const QString &value, WidgetSetMode mode)
Set the Z value on the widget.
void setY(const QString &value, WidgetSetMode mode)
Set the Y value on the widget.
bool cadEnabled() const
determines if CAD tools are enabled or if map tools behaves "nomally"
bool applyConstraints(QgsMapMouseEvent *e)
apply the CAD constraints.
void setEnabledM(bool enable)
Sets whether M is enabled.
int pointsCount() const
The number of points in the CAD point helper list.
void addPoint(const QgsPointXY &point)
Adds point to the CAD point list.
void releaseLocks(bool releaseRepeatingLocks=true)
unlock all constraints
void switchZM()
Determines if Z or M will be enabled.
void relativeMChanged(bool relative)
Emitted whenever the M parameter is toggled between absolute and relative.
void lockXChanged(bool locked)
Emitted whenever the X parameter is locked.
void focusOnXRequested()
Emitted whenever the X field should get the focus using the shortcuts (X).
void valueYChanged(const QString &value)
Emitted whenever the Y value changes (either the mouse moved, or the user changed the input).
void focusOnYRequested()
Emitted whenever the Y field should get the focus using the shortcuts (Y).
double getLineM() const
Convenient method to get the M value from the line edit wiget.
void enabledChangedDistance(bool enabled)
Emitted whenever the distance field is enabled or disabled.
void valueZChanged(const QString &value)
Emitted whenever the Z value changes (either the mouse moved, or the user changed the input).
void clearPoints()
Removes all points from the CAD point list.
QgsPoint previousPointV2(bool *exists=nullptr) const
The previous point.
void lockAngleChanged(bool locked)
Emitted whenever the angle parameter is locked.
void updateCadPaintItem()
Updates canvas item that displays constraints on the ma.
void removePreviousPoint()
Remove previous point in the CAD point list.
QgsPoint penultimatePointV2(bool *exists=nullptr) const
The penultimate point.
void pointChangedV2(const QgsPoint &point)
Sometimes a constraint may change the current point out of a mouse event.
void relativeXChanged(bool relative)
Emitted whenever the X parameter is toggled between absolute and relative.
void focusOnAngleRequested()
Emitted whenever the angle field should get the focus using the shortcuts (A).
WidgetSetMode
Type of interaction to simulate when editing values from external widget.
void focusOnZRequested()
Emitted whenever the Z field should get the focus using the shortcuts (Z).
void focusOnMRequested()
Emitted whenever the M field should get the focus using the shortcuts (M).
void popWarning()
Remove any previously emitted warnings (if any)
void valueXChanged(const QString &value)
Emitted whenever the X value changes (either the mouse moved, or the user changed the input).
double getLineZ() const
Convenient method to get the Z value from the line edit wiget.
void lockMChanged(bool locked)
Emitted whenever the M parameter is locked.
void cadEnabledChanged(bool enabled)
Signals for external widgets that need to update according to current values.
void lockYChanged(bool locked)
Emitted whenever the Y parameter is locked.
void valueAngleChanged(const QString &value)
Emitted whenever the angle value changes (either the mouse moved, or the user changed the input).
void valueMChanged(const QString &value)
Emitted whenever the M value changes (either the mouse moved, or the user changed the input).
void enable()
Enables the tool (call this when an appropriate map tool is set and in the condition to make use of c...
void relativeZChanged(bool relative)
Emitted whenever the Z parameter is toggled between absolute and relative.
@ RelativeAngle
Also for parallel and perpendicular.
@ RelativeCoordinates
This corresponds to distance and relative coordinates.
bool canvasKeyPressEventFilter(QKeyEvent *e)
Filter key events to e.g.
void setAngle(const QString &value, WidgetSetMode mode)
Set the angle value on the widget.
void enabledChangedAngle(bool enabled)
Emitted whenever the angle field is enabled or disabled.
void enabledChangedZ(bool enabled)
Emitted whenever the Z field is enabled or disabled.
void lockDistanceChanged(bool locked)
Emitted whenever the distance parameter is locked.
void relativeAngleChanged(bool relative)
Emitted whenever the angleX parameter is toggled between absolute and relative.
void enabledChangedM(bool enabled)
Emitted whenever the M field is enabled or disabled.
void setDistance(const QString &value, WidgetSetMode mode)
Set the distance value on the widget.
bool alignToSegment(QgsMapMouseEvent *e, QgsAdvancedDigitizingDockWidget::CadConstraint::LockMode lockMode=QgsAdvancedDigitizingDockWidget::CadConstraint::HardLock)
align to segment for additional constraint.
void setX(const QString &value, WidgetSetMode mode)
Set the X value on the widget.
QgsPoint currentPointV2(bool *exists=nullptr) const
The last point.
QgsAdvancedDigitizingDockWidget(QgsMapCanvas *canvas, QWidget *parent=nullptr)
Create an advanced digitizing dock widget.
void clear()
Clear any cached previous clicks and helper lines.
void focusOnDistanceRequested()
Emitted whenever the distance field should get the focus using the shortcuts (D).
QgsPoint currentPointLayerCoordinates(QgsMapLayer *layer) const
Returns the last CAD point, in a map layer's coordinates.
void setM(const QString &value, WidgetSetMode mode)
Set the M value on the widget.
void pushWarning(const QString &message)
Push a warning.
void relativeYChanged(bool relative)
Emitted whenever the Y parameter is toggled between absolute and relative.
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.
Definition: qgscadutils.h:42
bool locked
Whether the constraint is active, i.e. should be considered.
Definition: qgscadutils.h:55
double value
Numeric value of the constraint (coordinate/distance in map units or angle in degrees)
Definition: qgscadutils.h:59
bool relative
Whether the value is relative to previous value.
Definition: qgscadutils.h:57
Defines constraints for the QgsCadUtils::alignMapPoint() method.
Definition: qgscadutils.h:99
QgsCadUtils::AlignMapPointConstraint yConstraint
Constraint for Y coordinate.
Definition: qgscadutils.h:109
QgsCadUtils::AlignMapPointConstraint xConstraint
Constraint for X coordinate.
Definition: qgscadutils.h:107
double mapUnitsPerPixel
Map units/pixel ratio from map canvas.
Definition: qgscadutils.h:104
void setCadPoints(const QList< QgsPoint > &points)
Sets the list of recent CAD points (in map coordinates).
Definition: qgscadutils.h:152
QgsCadUtils::AlignMapPointConstraint mConstraint
Constraint for M coordinate.
Definition: qgscadutils.h:121
QgsCadUtils::AlignMapPointConstraint distanceConstraint
Constraint for distance.
Definition: qgscadutils.h:123
QgsCadUtils::AlignMapPointConstraint zConstraint
Constraint for Z coordinate.
Definition: qgscadutils.h:115
QgsSnappingUtils * snappingUtils
Snapping utils that will be used to snap point to map. Must not be nullptr.
Definition: qgscadutils.h:102
QgsCadUtils::AlignMapPointConstraint commonAngleConstraint
Constraint for soft lock to a common angle.
Definition: qgscadutils.h:127
QgsCadUtils::AlignMapPointConstraint angleConstraint
Constraint for angle.
Definition: qgscadutils.h:125
Structure returned from alignMapPoint() method.
Definition: qgscadutils.h:68
QgsPointXY finalMapPoint
map point aligned according to the constraints
Definition: qgscadutils.h:75
bool valid
Whether the combination of constraints is actually valid.
Definition: qgscadutils.h:72
QgsPointLocator::Match snapMatch
Snapped point - only valid if actually used for something.
Definition: qgscadutils.h:81
double softLockCommonAngle
Angle (in degrees) to which we have soft-locked ourselves (if not set it is -1)
Definition: qgscadutils.h:90
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.
Definition: qgscadutils.cpp:37
QgsDockWidget subclass with more fine-grained control over how the widget is closed or opened.
Definition: qgsdockwidget.h:32
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.
Definition: qgsmapcanvas.h:90
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.
Definition: qgsmaplayer.h:73
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.
The QgsMapToolAdvancedDigitizing class is a QgsMapTool which gives event directly in map coordinates ...
Base class for map tools that edit vector geometry.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:97
bool isEditable() const override
Returns true if the layer can be edited.
A class to represent a 2D point.
Definition: qgspointxy.h:59
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
void setX(double x) SIP_HOLDGIL
Sets the point's x-coordinate.
Definition: qgspoint.h:280
Q_GADGET double x
Definition: qgspoint.h:52
void setY(double y) SIP_HOLDGIL
Sets the point's y-coordinate.
Definition: qgspoint.h:291
void setZ(double z) SIP_HOLDGIL
Sets the point's z-coordinate.
Definition: qgspoint.h:304
double z
Definition: qgspoint.h:54
double distanceSquared(double x, double y) const SIP_HOLDGIL
Returns the Cartesian 2D squared distance between this point a specified x, y coordinate.
Definition: qgspoint.h:367
double m
Definition: qgspoint.h:55
double y
Definition: qgspoint.h:53
void setM(double m) SIP_HOLDGIL
Sets the point's m-value.
Definition: qgspoint.h:319
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:470
void snappingConfigChanged(const QgsSnappingConfig &config)
Emitted whenever the configuration for snapping has changed.
QgsSnappingConfig snappingConfig
Definition: qgsproject.h:110
bool topologicalEditing
Definition: qgsproject.h:117
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
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.
@ AllLayers
On all vector layers.
@ SegmentFlag
On segments.
void setTypeFlag(QgsSnappingConfig::SnappingTypeFlag type)
define the type of snapping
void setMode(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.
QgsSnappingConfig config
QgsPointLocator::Match snapToMap(QPoint point, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Snap to map according to the current configuration.
void setConfig(const QgsSnappingConfig &snappingConfig)
The snapping configuration controls the behavior of this object.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
static bool hasM(Type type) SIP_HOLDGIL
Tests whether a WKB type contains m values.
Definition: qgswkbtypes.h:1130
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:70
static bool hasZ(Type type) SIP_HOLDGIL
Tests whether a WKB type contains the z-dimension.
Definition: qgswkbtypes.h:1080
@ PointCloudLayer
Point cloud layer. Added in QGIS 3.18.
@ MeshLayer
Mesh layer. Added in QGIS 3.2.
@ VectorLayer
Vector layer.
@ RasterLayer
Raster layer.
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ PluginLayer
Plugin based layer.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
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...
Definition: qgis.cpp:71
QLineF segment(int index, QRectF rect, double radius)
int precision
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.