QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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 
351  break;
352  }
353  }
354 
355  setEnabledZ( enableZ );
356  setEnabledM( enableM );
357 }
358 
360 {
361  mRelativeZButton->setEnabled( enable );
362  mZLabel->setEnabled( enable );
363  mZLineEdit->setEnabled( enable );
364  if ( mZLineEdit->isEnabled() )
365  mZLineEdit->setText( QLocale().toString( QgsMapToolEdit( mMapCanvas ).defaultZValue(), 'f', 6 ) );
366  else
367  mZLineEdit->clear();
368  mLockZButton->setEnabled( enable );
369  emit enabledChangedZ( enable );
370 }
371 
373 {
374  mRelativeMButton->setEnabled( enable );
375  mMLabel->setEnabled( enable );
376  mMLineEdit->setEnabled( enable );
377  if ( mMLineEdit->isEnabled() )
378  mMLineEdit->setText( QLocale().toString( QgsMapToolEdit( mMapCanvas ).defaultMValue(), 'f', 6 ) );
379  else
380  mMLineEdit->clear();
381  mLockMButton->setEnabled( enable );
382  emit enabledChangedM( enable );
383 }
384 
385 void QgsAdvancedDigitizingDockWidget::activateCad( bool enabled )
386 {
387  enabled &= mCurrentMapToolSupportsCad;
388 
389  mSessionActive = enabled;
390 
391  if ( enabled && !isVisible() )
392  {
393  show();
394  }
395 
396  setCadEnabled( enabled );
397 }
398 
399 void QgsAdvancedDigitizingDockWidget::additionalConstraintClicked( bool activated )
400 {
401  if ( !activated )
402  {
403  lockAdditionalConstraint( AdditionalConstraint::NoConstraint );
404  }
405  else if ( sender() == mParallelAction )
406  {
407  lockAdditionalConstraint( AdditionalConstraint::Parallel );
408  }
409  else if ( sender() == mPerpendicularAction )
410  {
411  lockAdditionalConstraint( AdditionalConstraint::Perpendicular );
412  }
413 }
414 
415 void QgsAdvancedDigitizingDockWidget::setConstraintRelative( bool activate )
416 {
417  if ( sender() == mRelativeAngleButton )
418  {
419  mAngleConstraint->setRelative( activate );
420  emit relativeAngleChanged( activate );
421  }
422  else if ( sender() == mRelativeXButton )
423  {
424  mXConstraint->setRelative( activate );
425  emit relativeXChanged( activate );
426  }
427  else if ( sender() == mRelativeYButton )
428  {
429  mYConstraint->setRelative( activate );
430  emit relativeYChanged( activate );
431  }
432  else if ( sender() == mRelativeZButton )
433  {
434  mZConstraint->setRelative( activate );
435  emit relativeZChanged( activate );
436  }
437  else if ( sender() == mRelativeMButton )
438  {
439  mMConstraint->setRelative( activate );
440  emit relativeMChanged( activate );
441  }
442 }
443 
444 void QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock( bool activate )
445 {
446  if ( sender() == mRepeatingLockDistanceButton )
447  {
448  mDistanceConstraint->setRepeatingLock( activate );
449  }
450  else if ( sender() == mRepeatingLockAngleButton )
451  {
452  mAngleConstraint->setRepeatingLock( activate );
453  }
454  else if ( sender() == mRepeatingLockXButton )
455  {
456  mXConstraint->setRepeatingLock( activate );
457  }
458  else if ( sender() == mRepeatingLockYButton )
459  {
460  mYConstraint->setRepeatingLock( activate );
461  }
462  else if ( sender() == mRepeatingLockZButton )
463  {
464  mZConstraint->setRepeatingLock( activate );
465  }
466  else if ( sender() == mRepeatingLockMButton )
467  {
468  mMConstraint->setRepeatingLock( activate );
469  }
470 }
471 
472 void QgsAdvancedDigitizingDockWidget::setConstructionMode( bool enabled )
473 {
474  mConstructionMode = enabled;
475  mConstructionModeAction->setChecked( enabled );
476 }
477 
478 void QgsAdvancedDigitizingDockWidget::settingsButtonTriggered( QAction *action )
479 {
480  // common angles
481  const QMap<QAction *, double>::const_iterator ica = mCommonAngleActions.constFind( action );
482  if ( ica != mCommonAngleActions.constEnd() )
483  {
484  ica.key()->setChecked( true );
485  mCommonAngleConstraint = ica.value();
486  QgsSettings().setValue( QStringLiteral( "/Cad/CommonAngle" ), ica.value() );
487  mSettingsAction->setChecked( mCommonAngleConstraint != 0 );
488  return;
489  }
490 }
491 
492 QgsMapLayer *QgsAdvancedDigitizingDockWidget::targetLayer()
493 {
494  if ( QgsMapToolAdvancedDigitizing *advancedTool = qobject_cast< QgsMapToolAdvancedDigitizing * >( mMapCanvas->mapTool() ) )
495  {
496  return advancedTool->layer();
497  }
498  else
499  {
500  return mMapCanvas->currentLayer();
501  }
502 }
503 
504 void QgsAdvancedDigitizingDockWidget::releaseLocks( bool releaseRepeatingLocks )
505 {
506  // release all locks except construction mode
507 
508  lockAdditionalConstraint( AdditionalConstraint::NoConstraint );
509 
510  if ( releaseRepeatingLocks || !mAngleConstraint->isRepeatingLock() )
511  {
512  mAngleConstraint->setLockMode( CadConstraint::NoLock );
513  emit lockAngleChanged( false );
514  }
515  if ( releaseRepeatingLocks || !mDistanceConstraint->isRepeatingLock() )
516  {
517  mDistanceConstraint->setLockMode( CadConstraint::NoLock );
518  emit lockDistanceChanged( false );
519  }
520  if ( releaseRepeatingLocks || !mXConstraint->isRepeatingLock() )
521  {
522  mXConstraint->setLockMode( CadConstraint::NoLock );
523  emit lockXChanged( false );
524  }
525  if ( releaseRepeatingLocks || !mYConstraint->isRepeatingLock() )
526  {
527  mYConstraint->setLockMode( CadConstraint::NoLock );
528  emit lockYChanged( false );
529  }
530  if ( releaseRepeatingLocks || !mZConstraint->isRepeatingLock() )
531  {
532  mZConstraint->setLockMode( CadConstraint::NoLock );
533  emit lockZChanged( false );
534  }
535  if ( releaseRepeatingLocks || !mMConstraint->isRepeatingLock() )
536  {
537  mMConstraint->setLockMode( CadConstraint::NoLock );
538  emit lockMChanged( false );
539  }
540 
541  if ( !mCadPointList.empty() )
542  {
543  if ( !mXConstraint->isLocked() && !mXConstraint->relative() )
544  {
545  mXConstraint->setValue( mCadPointList.constLast().x(), true );
546  }
547  if ( !mYConstraint->isLocked() && !mYConstraint->relative() )
548  {
549  mYConstraint->setValue( mCadPointList.constLast().y(), true );
550  }
551  if ( !mZConstraint->isLocked() && !mZConstraint->relative() )
552  {
553  mZConstraint->setValue( mCadPointList.constLast().z(), true );
554  }
555  if ( !mMConstraint->isLocked() && !mMConstraint->relative() )
556  {
557  mMConstraint->setValue( mCadPointList.constLast().m(), true );
558  }
559  }
560 
561 }
562 
563 #if 0
564 void QgsAdvancedDigitizingDockWidget::emit pointChanged()
565 {
566  // run a fake map mouse event to update the paint item
567  QPoint globalPos = mMapCanvas->cursor().pos();
568  QPoint pos = mMapCanvas->mapFromGlobal( globalPos );
569  QMouseEvent *e = new QMouseEvent( QEvent::MouseMove, pos, globalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier );
570  mCurrentMapTool->canvasMoveEvent( e );
571 }
572 #endif
573 
574 QgsAdvancedDigitizingDockWidget::CadConstraint *QgsAdvancedDigitizingDockWidget::objectToConstraint( const QObject *obj ) const
575 {
576  CadConstraint *constraint = nullptr;
577  if ( obj == mAngleLineEdit || obj == mLockAngleButton )
578  {
579  constraint = mAngleConstraint.get();
580  }
581  else if ( obj == mDistanceLineEdit || obj == mLockDistanceButton )
582  {
583  constraint = mDistanceConstraint.get();
584  }
585  else if ( obj == mXLineEdit || obj == mLockXButton )
586  {
587  constraint = mXConstraint.get();
588  }
589  else if ( obj == mYLineEdit || obj == mLockYButton )
590  {
591  constraint = mYConstraint.get();
592  }
593  else if ( obj == mZLineEdit || obj == mLockZButton )
594  {
595  constraint = mZConstraint.get();
596  }
597  else if ( obj == mMLineEdit || obj == mLockMButton )
598  {
599  constraint = mMConstraint.get();
600  }
601  return constraint;
602 }
603 
604 double QgsAdvancedDigitizingDockWidget::parseUserInput( const QString &inputValue, bool &ok ) const
605 {
606  ok = false;
607  double value = qgsPermissiveToDouble( inputValue, ok );
608  if ( ok )
609  {
610  return value;
611  }
612  else
613  {
614  // try to evaluate expression
615  QgsExpression expr( inputValue );
616  const QVariant result = expr.evaluate();
617  if ( expr.hasEvalError() )
618  ok = false;
619  else
620  value = result.toDouble( &ok );
621  return value;
622  }
623 }
624 
625 void QgsAdvancedDigitizingDockWidget::updateConstraintValue( CadConstraint *constraint, const QString &textValue, bool convertExpression )
626 {
627  if ( !constraint || textValue.isEmpty() )
628  {
629  return;
630  }
631 
632  if ( constraint->lockMode() == CadConstraint::NoLock )
633  return;
634 
635  bool ok;
636  const double value = parseUserInput( textValue, ok );
637  if ( !ok )
638  return;
639 
640  constraint->setValue( value, convertExpression );
641  // run a fake map mouse event to update the paint item
642  emit pointChangedV2( mCadPointList.value( 0 ) );
643 }
644 
645 void QgsAdvancedDigitizingDockWidget::lockConstraint( bool activate /* default true */ )
646 {
647  CadConstraint *constraint = objectToConstraint( sender() );
648  if ( !constraint )
649  {
650  return;
651  }
652 
653  if ( activate )
654  {
655  const QString textValue = constraint->lineEdit()->text();
656  if ( !textValue.isEmpty() )
657  {
658  bool ok;
659  const double value = parseUserInput( textValue, ok );
660  if ( ok )
661  {
662  constraint->setValue( value );
663  }
664  else
665  {
666  activate = false;
667  }
668  }
669  else
670  {
671  activate = false;
672  }
673  }
674  constraint->setLockMode( activate ? CadConstraint::HardLock : CadConstraint::NoLock );
675 
676  if ( constraint == mXConstraint.get() )
677  {
678  emit lockXChanged( activate );
679  }
680  else if ( constraint == mYConstraint.get() )
681  {
682  emit lockYChanged( activate );
683  }
684  else if ( constraint == mZConstraint.get() )
685  {
686  emit lockZChanged( activate );
687  }
688  else if ( constraint == mMConstraint.get() )
689  {
690  emit lockMChanged( activate );
691  }
692  else if ( constraint == mDistanceConstraint.get() )
693  {
694  emit lockDistanceChanged( activate );
695  }
696  else if ( constraint == mAngleConstraint.get() )
697  {
698  emit lockAngleChanged( activate );
699  }
700 
701  if ( activate )
702  {
703  // deactivate perpendicular/parallel if angle has been activated
704  if ( constraint == mAngleConstraint.get() )
705  {
706  lockAdditionalConstraint( AdditionalConstraint::NoConstraint );
707  }
708 
709  // run a fake map mouse event to update the paint item
710  emit pointChangedV2( mCadPointList.value( 0 ) );
711  }
712 }
713 
714 void QgsAdvancedDigitizingDockWidget::constraintTextEdited( const QString &textValue )
715 {
716  CadConstraint *constraint = objectToConstraint( sender() );
717  if ( !constraint )
718  {
719  return;
720  }
721 
722  updateConstraintValue( constraint, textValue, false );
723 }
724 
725 void QgsAdvancedDigitizingDockWidget::constraintFocusOut()
726 {
727  QLineEdit *lineEdit = qobject_cast< QLineEdit * >( sender()->parent() );
728  if ( !lineEdit )
729  return;
730 
731  CadConstraint *constraint = objectToConstraint( lineEdit );
732  if ( !constraint )
733  {
734  return;
735  }
736 
737  updateConstraintValue( constraint, lineEdit->text(), true );
738 }
739 
740 void QgsAdvancedDigitizingDockWidget::lockAdditionalConstraint( AdditionalConstraint constraint )
741 {
742  mAdditionalConstraint = constraint;
743  mPerpendicularAction->setChecked( constraint == AdditionalConstraint::Perpendicular );
744  mParallelAction->setChecked( constraint == AdditionalConstraint::Parallel );
745 }
746 
747 void QgsAdvancedDigitizingDockWidget::updateCapacity( bool updateUIwithoutChange )
748 {
749  CadCapacities newCapacities = CadCapacities();
750  const bool isGeographic = mMapCanvas->mapSettings().destinationCrs().isGeographic();
751  if ( !isGeographic )
752  newCapacities |= Distance;
753 
754  // first point is the mouse point (it doesn't count)
755  if ( mCadPointList.count() > 1 )
756  {
757  newCapacities |= RelativeCoordinates;
758  if ( !isGeographic )
759  newCapacities |= AbsoluteAngle;
760  }
761  if ( mCadPointList.count() > 2 )
762  {
763  if ( !isGeographic )
764  newCapacities |= RelativeAngle;
765  }
766  if ( !updateUIwithoutChange && newCapacities == mCapacities )
767  {
768  return;
769  }
770 
771  const bool snappingEnabled = QgsProject::instance()->snappingConfig().enabled();
772 
773  // update the UI according to new capacities
774  // still keep the old to compare
775 
776  const bool distance = mCadEnabled && newCapacities.testFlag( Distance );
777  const bool relativeAngle = mCadEnabled && newCapacities.testFlag( RelativeAngle );
778  const bool absoluteAngle = mCadEnabled && newCapacities.testFlag( AbsoluteAngle );
779  const bool relativeCoordinates = mCadEnabled && newCapacities.testFlag( RelativeCoordinates );
780 
781  mPerpendicularAction->setEnabled( distance && absoluteAngle && snappingEnabled );
782  mParallelAction->setEnabled( distance && absoluteAngle && snappingEnabled );
783 
784  //update tooltips on buttons
785  if ( !snappingEnabled )
786  {
787  mPerpendicularAction->setToolTip( tr( "Snapping must be enabled to utilize perpendicular mode" ) );
788  mParallelAction->setToolTip( tr( "Snapping must be enabled to utilize parallel mode" ) );
789  }
790  else
791  {
792  mPerpendicularAction->setToolTip( "<b>" + tr( "Perpendicular" ) + "</b><br>(" + tr( "press p to switch between perpendicular, parallel and normal mode" ) + ")" );
793  mParallelAction->setToolTip( "<b>" + tr( "Parallel" ) + "</b><br>(" + tr( "press p to switch between perpendicular, parallel and normal mode" ) + ")" );
794  }
795 
796 
797  if ( !absoluteAngle )
798  {
799  lockAdditionalConstraint( AdditionalConstraint::NoConstraint );
800  }
801 
802  // absolute angle = azimuth, relative = from previous line
803  mLockAngleButton->setEnabled( absoluteAngle );
804  mRelativeAngleButton->setEnabled( relativeAngle );
805  mAngleLineEdit->setEnabled( absoluteAngle );
806  emit enabledChangedAngle( absoluteAngle );
807  if ( !absoluteAngle )
808  {
809  mAngleConstraint->setLockMode( CadConstraint::NoLock );
810  }
811  if ( !relativeAngle )
812  {
813  mAngleConstraint->setRelative( false );
814  emit relativeAngleChanged( false );
815  }
816  else if ( relativeAngle && !mCapacities.testFlag( RelativeAngle ) )
817  {
818  // set angle mode to relative if can do and wasn't available before
819  mAngleConstraint->setRelative( true );
820  emit relativeAngleChanged( true );
821  }
822 
823  // distance is always relative
824  mLockDistanceButton->setEnabled( distance && relativeCoordinates );
825  mDistanceLineEdit->setEnabled( distance && relativeCoordinates );
826  emit enabledChangedDistance( distance && relativeCoordinates );
827  if ( !( distance && relativeCoordinates ) )
828  {
829  mDistanceConstraint->setLockMode( CadConstraint::NoLock );
830  }
831 
832  mRelativeXButton->setEnabled( relativeCoordinates );
833  mRelativeYButton->setEnabled( relativeCoordinates );
834  mRelativeZButton->setEnabled( relativeCoordinates );
835  mRelativeMButton->setEnabled( relativeCoordinates );
836 
837  // update capacities
838  mCapacities = newCapacities;
839 }
840 
841 
843 {
846  constr.relative = c->relative();
847  constr.value = c->value();
848  return constr;
849 }
850 
852 {
854  context.snappingUtils = mMapCanvas->snappingUtils();
855  context.mapUnitsPerPixel = mMapCanvas->mapUnitsPerPixel();
856  context.xConstraint = _constraint( mXConstraint.get() );
857  context.yConstraint = _constraint( mYConstraint.get() );
858  context.zConstraint = _constraint( mZConstraint.get() );
859  context.mConstraint = _constraint( mMConstraint.get() );
860  context.distanceConstraint = _constraint( mDistanceConstraint.get() );
861  context.angleConstraint = _constraint( mAngleConstraint.get() );
862  context.setCadPoints( mCadPointList );
863 
866  context.commonAngleConstraint.value = mCommonAngleConstraint;
867 
869 
870  const bool res = output.valid;
871  QgsPoint point = pointXYToPoint( output.finalMapPoint );
872  mSnappedSegment.clear();
873  if ( output.snapMatch.hasEdge() )
874  {
875  QgsPointXY edgePt0, edgePt1;
876  output.snapMatch.edgePoints( edgePt0, edgePt1 );
877  mSnappedSegment << edgePt0 << edgePt1;
878  }
879  if ( mAngleConstraint->lockMode() != CadConstraint::HardLock )
880  {
881  if ( output.softLockCommonAngle != -1 )
882  {
883  mAngleConstraint->setLockMode( CadConstraint::SoftLock );
884  mAngleConstraint->setValue( output.softLockCommonAngle );
885  }
886  else
887  {
888  mAngleConstraint->setLockMode( CadConstraint::NoLock );
889  }
890  }
891 
892  if ( output.snapMatch.isValid() )
893  {
894  mSnapIndicator->setMatch( output.snapMatch );
895  mSnapIndicator->setVisible( true );
896  }
897  else
898  {
899  mSnapIndicator->setVisible( false );
900  }
901 
902  /*
903  * Ensure that Z and M are passed
904  * It will be dropped as needed later.
905  */
906  point.setZ( QgsMapToolEdit( mMapCanvas ).defaultZValue() );
907  point.setM( QgsMapToolEdit( mMapCanvas ).defaultMValue() );
908 
909  /*
910  * Constraints are applied in 2D, they are always called when using the tool
911  * but they do not take into account if when you snap on a vertex it has
912  * a Z value.
913  * To get the value we use the snapPoint method. However, we only apply it
914  * when the snapped point corresponds to the constrained point or on an edge
915  * if the topological editing is activated.
916  */
917  e->setMapPoint( point );
918  mSnapMatch = context.snappingUtils->snapToMap( point, nullptr, true );
919  if ( ( ( mSnapMatch.hasVertex() || mSnapMatch.hasLineEndpoint() ) && ( point == mSnapMatch.point() ) ) || ( mSnapMatch.hasEdge() && QgsProject::instance()->topologicalEditing() ) )
920  {
921  e->snapPoint();
922  point = mSnapMatch.interpolatedPoint();
923  }
924 
925  /*
926  * And if M or Z lock button is activated get the value of the input.
927  */
928  if ( mLockZButton->isChecked() )
929  {
930  point.setZ( QLocale().toDouble( mZLineEdit->text() ) );
931  }
932  if ( mLockMButton->isChecked() )
933  {
934  point.setM( QLocale().toDouble( mMLineEdit->text() ) );
935  }
936 
937  // update the point list
938  updateCurrentPoint( point );
939 
940  updateUnlockedConstraintValues( point );
941 
942  if ( res )
943  {
944  emit popWarning();
945  }
946  else
947  {
948  emit pushWarning( tr( "Some constraints are incompatible. Resulting point might be incorrect." ) );
949  }
950 
951  return res;
952 }
953 
954 
955 void QgsAdvancedDigitizingDockWidget::updateUnlockedConstraintValues( const QgsPoint &point )
956 {
957  bool previousPointExist, penulPointExist;
958  const QgsPoint previousPt = previousPointV2( &previousPointExist );
959  const QgsPoint penultimatePt = penultimatePointV2( &penulPointExist );
960 
961  // --- angle
962  if ( !mAngleConstraint->isLocked() && previousPointExist )
963  {
964  double angle = 0.0;
965  if ( penulPointExist && mAngleConstraint->relative() )
966  {
967  // previous angle
968  angle = std::atan2( previousPt.y() - penultimatePt.y(),
969  previousPt.x() - penultimatePt.x() );
970  }
971  angle = ( std::atan2( point.y() - previousPt.y(),
972  point.x() - previousPt.x()
973  ) - angle ) * 180 / M_PI;
974  // modulus
975  angle = std::fmod( angle, 360.0 );
976  mAngleConstraint->setValue( angle );
977  }
978  // --- distance
979  if ( !mDistanceConstraint->isLocked() && previousPointExist )
980  {
981  mDistanceConstraint->setValue( std::sqrt( previousPt.distanceSquared( point ) ) );
982  }
983  // --- X
984  if ( !mXConstraint->isLocked() )
985  {
986  if ( previousPointExist && mXConstraint->relative() )
987  {
988  mXConstraint->setValue( point.x() - previousPt.x() );
989  }
990  else
991  {
992  mXConstraint->setValue( point.x() );
993  }
994  }
995  // --- Y
996  if ( !mYConstraint->isLocked() )
997  {
998  if ( previousPointExist && mYConstraint->relative() )
999  {
1000  mYConstraint->setValue( point.y() - previousPt.y() );
1001  }
1002  else
1003  {
1004  mYConstraint->setValue( point.y() );
1005  }
1006  }
1007  // --- Z
1008  if ( !mZConstraint->isLocked() )
1009  {
1010  if ( previousPointExist && mZConstraint->relative() )
1011  {
1012  mZConstraint->setValue( point.z() - previousPt.z() );
1013  }
1014  else
1015  {
1016  mZConstraint->setValue( point.z() );
1017  }
1018  }
1019  // --- M
1020  if ( !mMConstraint->isLocked() )
1021  {
1022  if ( previousPointExist && mMConstraint->relative() )
1023  {
1024  mMConstraint->setValue( point.m() - previousPt.m() );
1025  }
1026  else
1027  {
1028  mMConstraint->setValue( point.m() );
1029  }
1030  }
1031 }
1032 
1033 
1034 QList<QgsPointXY> QgsAdvancedDigitizingDockWidget::snapSegmentToAllLayers( const QgsPointXY &originalMapPoint, bool *snapped ) const
1035 {
1036  QList<QgsPointXY> segment;
1037  QgsPointXY pt1, pt2;
1038  QgsPointLocator::Match match;
1039 
1040  QgsSnappingUtils *snappingUtils = mMapCanvas->snappingUtils();
1041 
1042  const QgsSnappingConfig canvasConfig = snappingUtils->config();
1043  QgsSnappingConfig localConfig = snappingUtils->config();
1044 
1045  localConfig.setMode( QgsSnappingConfig::AllLayers );
1047  snappingUtils->setConfig( localConfig );
1048 
1049  match = snappingUtils->snapToMap( originalMapPoint, nullptr, true );
1050 
1051  snappingUtils->setConfig( canvasConfig );
1052 
1053  if ( match.isValid() && match.hasEdge() )
1054  {
1055  match.edgePoints( pt1, pt2 );
1056  segment << pt1 << pt2;
1057  }
1058 
1059  if ( snapped )
1060  {
1061  *snapped = segment.count() == 2;
1062  }
1063 
1064  return segment;
1065 }
1066 
1068 {
1069  if ( mAdditionalConstraint == AdditionalConstraint::NoConstraint )
1070  {
1071  return false;
1072  }
1073 
1074  bool previousPointExist, penulPointExist, snappedSegmentExist;
1075  const QgsPoint previousPt = previousPointV2( &previousPointExist );
1076  const QgsPoint penultimatePt = penultimatePointV2( &penulPointExist );
1077  mSnappedSegment = snapSegmentToAllLayers( e->originalMapPoint(), &snappedSegmentExist );
1078 
1079  if ( !previousPointExist || !snappedSegmentExist )
1080  {
1081  return false;
1082  }
1083 
1084  double angle = std::atan2( mSnappedSegment[0].y() - mSnappedSegment[1].y(), mSnappedSegment[0].x() - mSnappedSegment[1].x() );
1085 
1086  if ( mAngleConstraint->relative() && penulPointExist )
1087  {
1088  angle -= std::atan2( previousPt.y() - penultimatePt.y(), previousPt.x() - penultimatePt.x() );
1089  }
1090 
1091  if ( mAdditionalConstraint == AdditionalConstraint::Perpendicular )
1092  {
1093  angle += M_PI_2;
1094  }
1095 
1096  angle *= 180 / M_PI;
1097 
1098  mAngleConstraint->setValue( angle );
1099  mAngleConstraint->setLockMode( lockMode );
1100  if ( lockMode == CadConstraint::HardLock )
1101  {
1102  mAdditionalConstraint = AdditionalConstraint::NoConstraint;
1103  }
1104 
1105  return true;
1106 }
1107 
1109 {
1110  // event on map tool
1111 
1112  if ( !mCadEnabled )
1113  return false;
1114 
1115  switch ( e->key() )
1116  {
1117  case Qt::Key_Backspace:
1118  case Qt::Key_Delete:
1119  {
1121  releaseLocks( false );
1122  break;
1123  }
1124  case Qt::Key_Escape:
1125  {
1126  releaseLocks();
1127  break;
1128  }
1129  default:
1130  {
1131  keyPressEvent( e );
1132  break;
1133  }
1134  }
1135  // for map tools, continues with key press in any case
1136  return false;
1137 }
1138 
1140 {
1141  clearPoints();
1142  releaseLocks();
1143 }
1144 
1146 {
1147  // event on dock (this)
1148 
1149  if ( !mCadEnabled )
1150  return;
1151 
1152  switch ( e->key() )
1153  {
1154  case Qt::Key_Backspace:
1155  case Qt::Key_Delete:
1156  {
1158  releaseLocks( false );
1159  break;
1160  }
1161  case Qt::Key_Escape:
1162  {
1163  releaseLocks();
1164  break;
1165  }
1166  default:
1167  {
1168  filterKeyPress( e );
1169  break;
1170  }
1171  }
1172 }
1173 
1174 void QgsAdvancedDigitizingDockWidget::setPoints( const QList<QgsPointXY> &points )
1175 {
1176  clearPoints();
1177  const auto constPoints = points;
1178  for ( const QgsPointXY &pt : constPoints )
1179  {
1180  addPoint( pt );
1181  }
1182 }
1183 
1184 bool QgsAdvancedDigitizingDockWidget::eventFilter( QObject *obj, QEvent *event )
1185 {
1186  if ( !cadEnabled() )
1187  {
1188  return QgsDockWidget::eventFilter( obj, event );
1189  }
1190 
1191  // event for line edits and map canvas
1192  // we have to catch both KeyPress events and ShortcutOverride events. This is because
1193  // the Ctrl+D and Ctrl+A shortcuts for locking distance/angle clash with the global
1194  // "remove layer" and "select all" shortcuts. Catching ShortcutOverride events allows
1195  // us to intercept these keystrokes before they are caught by the global shortcuts
1196  if ( event->type() == QEvent::ShortcutOverride || event->type() == QEvent::KeyPress )
1197  {
1198  if ( QKeyEvent *keyEvent = dynamic_cast<QKeyEvent *>( event ) )
1199  {
1200  return filterKeyPress( keyEvent );
1201  }
1202  }
1203  return QgsDockWidget::eventFilter( obj, event );
1204 }
1205 
1206 bool QgsAdvancedDigitizingDockWidget::filterKeyPress( QKeyEvent *e )
1207 {
1208  // we need to be careful here -- because this method is called on both KeyPress events AND
1209  // ShortcutOverride events, we have to take care that we don't trigger the handling for BOTH
1210  // these event types for a single key press. I.e. pressing "A" may first call trigger a
1211  // ShortcutOverride event (sometimes, not always!) followed immediately by a KeyPress event.
1212  const QEvent::Type type = e->type();
1213  switch ( e->key() )
1214  {
1215  case Qt::Key_X:
1216  {
1217  // modifier+x ONLY caught for ShortcutOverride events...
1218  if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1219  {
1220  mXConstraint->toggleLocked();
1221  emit lockXChanged( mXConstraint->isLocked() );
1222  emit pointChangedV2( mCadPointList.value( 0 ) );
1223  e->accept();
1224  }
1225  else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1226  {
1227  if ( mCapacities.testFlag( RelativeCoordinates ) )
1228  {
1229  mXConstraint->toggleRelative();
1230  emit relativeXChanged( mXConstraint->relative() );
1231  emit pointChangedV2( mCadPointList.value( 0 ) );
1232  e->accept();
1233  }
1234  }
1235  // .. but "X" alone ONLY caught for KeyPress events (see comment at start of function)
1236  else if ( type == QEvent::KeyPress )
1237  {
1238  mXLineEdit->setFocus();
1239  mXLineEdit->selectAll();
1240  emit focusOnXRequested();
1241  e->accept();
1242  }
1243  break;
1244  }
1245  case Qt::Key_Y:
1246  {
1247  // modifier+y ONLY caught for ShortcutOverride events...
1248  if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1249  {
1250  mYConstraint->toggleLocked();
1251  emit lockYChanged( mYConstraint->isLocked() );
1252  emit pointChangedV2( mCadPointList.value( 0 ) );
1253  e->accept();
1254  }
1255  else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1256  {
1257  if ( mCapacities.testFlag( RelativeCoordinates ) )
1258  {
1259  mYConstraint->toggleRelative();
1260  emit relativeYChanged( mYConstraint->relative() );
1261  emit pointChangedV2( mCadPointList.value( 0 ) );
1262  e->accept();
1263  }
1264  }
1265  // .. but "y" alone ONLY caught for KeyPress events (see comment at start of function)
1266  else if ( type == QEvent::KeyPress )
1267  {
1268  mYLineEdit->setFocus();
1269  mYLineEdit->selectAll();
1270  emit focusOnYRequested();
1271  e->accept();
1272  }
1273  break;
1274  }
1275  case Qt::Key_Z:
1276  {
1277  // modifier+z ONLY caught for ShortcutOverride events...
1278  if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::AltModifier )
1279  {
1280  mZConstraint->toggleLocked();
1281  emit lockZChanged( mZConstraint->isLocked() );
1282  emit pointChangedV2( mCadPointList.value( 0 ) );
1283  e->accept();
1284  }
1285  else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1286  {
1287  if ( mCapacities.testFlag( RelativeCoordinates ) )
1288  {
1289  mZConstraint->toggleRelative();
1290  emit relativeZChanged( mZConstraint->relative() );
1291  emit pointChangedV2( mCadPointList.value( 0 ) );
1292  e->accept();
1293  }
1294  }
1295  // .. but "z" alone ONLY caught for KeyPress events (see comment at start of function)
1296  else if ( type == QEvent::KeyPress )
1297  {
1298  mZLineEdit->setFocus();
1299  mZLineEdit->selectAll();
1300  emit focusOnZRequested();
1301  e->accept();
1302  }
1303  break;
1304  }
1305  case Qt::Key_M:
1306  {
1307  // modifier+m ONLY caught for ShortcutOverride events...
1308  if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1309  {
1310  mMConstraint->toggleLocked();
1311  emit lockMChanged( mMConstraint->isLocked() );
1312  emit pointChangedV2( mCadPointList.value( 0 ) );
1313  e->accept();
1314  }
1315  else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1316  {
1317  if ( mCapacities.testFlag( RelativeCoordinates ) )
1318  {
1319  mMConstraint->toggleRelative();
1320  emit relativeMChanged( mMConstraint->relative() );
1321  emit pointChangedV2( mCadPointList.value( 0 ) );
1322  e->accept();
1323  }
1324  }
1325  // .. but "m" alone ONLY caught for KeyPress events (see comment at start of function)
1326  else if ( type == QEvent::KeyPress )
1327  {
1328  mMLineEdit->setFocus();
1329  mMLineEdit->selectAll();
1330  emit focusOnMRequested();
1331  e->accept();
1332  }
1333  break;
1334  }
1335  case Qt::Key_A:
1336  {
1337  // modifier+a ONLY caught for ShortcutOverride events...
1338  if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1339  {
1340  if ( mCapacities.testFlag( AbsoluteAngle ) )
1341  {
1342  mAngleConstraint->toggleLocked();
1343  emit lockAngleChanged( mAngleConstraint->isLocked() );
1344  emit pointChangedV2( mCadPointList.value( 0 ) );
1345  e->accept();
1346  }
1347  }
1348  else if ( type == QEvent::ShortcutOverride && e->modifiers() == Qt::ShiftModifier )
1349  {
1350  if ( mCapacities.testFlag( RelativeAngle ) )
1351  {
1352  mAngleConstraint->toggleRelative();
1353  emit relativeAngleChanged( mAngleConstraint->relative() );
1354  emit pointChangedV2( mCadPointList.value( 0 ) );
1355  e->accept();
1356  }
1357  }
1358  // .. but "a" alone ONLY caught for KeyPress events (see comment at start of function)
1359  else if ( type == QEvent::KeyPress )
1360  {
1361  mAngleLineEdit->setFocus();
1362  mAngleLineEdit->selectAll();
1363  emit focusOnAngleRequested();
1364  e->accept();
1365  }
1366  break;
1367  }
1368  case Qt::Key_D:
1369  {
1370  // modifier+d ONLY caught for ShortcutOverride events...
1371  if ( type == QEvent::ShortcutOverride && ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier ) )
1372  {
1373  if ( mCapacities.testFlag( RelativeCoordinates ) && mCapacities.testFlag( Distance ) )
1374  {
1375  mDistanceConstraint->toggleLocked();
1376  emit lockDistanceChanged( mDistanceConstraint->isLocked() );
1377  emit pointChangedV2( mCadPointList.value( 0 ) );
1378  e->accept();
1379  }
1380  }
1381  // .. but "d" alone ONLY caught for KeyPress events (see comment at start of function)
1382  else if ( type == QEvent::KeyPress )
1383  {
1384  mDistanceLineEdit->setFocus();
1385  mDistanceLineEdit->selectAll();
1386  emit focusOnDistanceRequested();
1387  e->accept();
1388  }
1389  break;
1390  }
1391  case Qt::Key_C:
1392  {
1393  if ( type == QEvent::KeyPress )
1394  {
1395  setConstructionMode( !mConstructionMode );
1396  e->accept();
1397  }
1398  break;
1399  }
1400  case Qt::Key_P:
1401  {
1402  if ( type == QEvent::KeyPress )
1403  {
1404  const bool parallel = mParallelAction->isChecked();
1405  const bool perpendicular = mPerpendicularAction->isChecked();
1406 
1407  if ( !parallel && !perpendicular )
1408  {
1409  lockAdditionalConstraint( AdditionalConstraint::Perpendicular );
1410  }
1411  else if ( perpendicular )
1412  {
1413  lockAdditionalConstraint( AdditionalConstraint::Parallel );
1414  }
1415  else
1416  {
1417  lockAdditionalConstraint( AdditionalConstraint::NoConstraint );
1418  }
1419  e->accept();
1420 
1421  // run a fake map mouse event to update the paint item
1422  emit pointChangedV2( mCadPointList.value( 0 ) );
1423  }
1424  break;
1425  }
1426  default:
1427  {
1428  return false; // continues
1429  }
1430  }
1431  return e->isAccepted();
1432 }
1433 
1435 {
1436  connect( mMapCanvas, &QgsMapCanvas::destinationCrsChanged, this, &QgsAdvancedDigitizingDockWidget::enable, Qt::UniqueConnection );
1437  if ( mMapCanvas->mapSettings().destinationCrs().isGeographic() )
1438  {
1439  mAngleLineEdit->setEnabled( false );
1440  mAngleLineEdit->setToolTip( tr( "Angle constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1441 
1442  mDistanceLineEdit->setEnabled( false );
1443  mDistanceLineEdit->setToolTip( tr( "Distance constraint cannot be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1444 
1445  mLabelX->setText( tr( "Long" ) );
1446  mLabelY->setText( tr( "Lat" ) );
1447 
1448  mXConstraint->setPrecision( 8 );
1449  mYConstraint->setPrecision( 8 );
1450  }
1451  else
1452  {
1453  mAngleLineEdit->setToolTip( "<b>" + tr( "Angle" ) + "</b><br>(" + tr( "press a for quick access" ) + ")" );
1454  mAngleLineEdit->setToolTip( QString() );
1455 
1456  mDistanceLineEdit->setEnabled( true );
1457  mDistanceLineEdit->setToolTip( "<b>" + tr( "Distance" ) + "</b><br>(" + tr( "press d for quick access" ) + ")" );
1458 
1459  mLabelX->setText( tr( "x" ) );
1460  mLabelY->setText( tr( "y" ) );
1461 
1462  mXConstraint->setPrecision( 6 );
1463  mYConstraint->setPrecision( 6 );
1464  }
1465 
1466  mEnableAction->setEnabled( true );
1467  mErrorLabel->hide();
1468  mCadWidget->show();
1469 
1470  mCurrentMapToolSupportsCad = true;
1471 
1472  if ( mSessionActive && !isVisible() )
1473  {
1474  show();
1475  }
1476  setCadEnabled( mSessionActive );
1477 }
1478 
1480 {
1482 
1483  mEnableAction->setEnabled( false );
1484  mErrorLabel->setText( tr( "CAD tools are not enabled for the current map tool" ) );
1485  mErrorLabel->show();
1486  mCadWidget->hide();
1487 
1488  mCurrentMapToolSupportsCad = false;
1489 
1490  setCadEnabled( false );
1491 }
1492 
1494 {
1495  mCadPaintItem->update();
1496 }
1497 
1499 {
1500  QgsPoint pt = pointXYToPoint( point );
1501  if ( !pointsCount() )
1502  {
1503  mCadPointList << pt;
1504  }
1505  else
1506  {
1507  mCadPointList.insert( 0, pt );
1508  }
1509 
1510  updateCapacity();
1512 }
1513 
1515 {
1516  if ( !pointsCount() )
1517  return;
1518 
1519  const int i = pointsCount() > 1 ? 1 : 0;
1520  mCadPointList.removeAt( i );
1521  updateCapacity();
1523 }
1524 
1526 {
1527  mCadPointList.clear();
1528  mSnappedSegment.clear();
1529 
1530  updateCapacity();
1532 }
1533 
1534 void QgsAdvancedDigitizingDockWidget::updateCurrentPoint( const QgsPoint &point )
1535 {
1536  if ( !pointsCount() )
1537  {
1538  mCadPointList << point;
1539  updateCapacity();
1540  }
1541  else
1542  {
1543  mCadPointList[0] = point;
1544  }
1546 }
1547 
1548 
1550 {
1551  mLockMode = mode;
1552  mLockerButton->setChecked( mode == HardLock );
1553  if ( mRepeatingLockButton )
1554  {
1555  if ( mode == HardLock )
1556  {
1557  mRepeatingLockButton->setEnabled( true );
1558  }
1559  else
1560  {
1561  mRepeatingLockButton->setChecked( false );
1562  mRepeatingLockButton->setEnabled( false );
1563  }
1564  }
1565 
1566  if ( mode == NoLock )
1567  {
1568  mLineEdit->clear();
1569  }
1570 
1571 }
1572 
1574 {
1575  mRepeatingLock = repeating;
1576  if ( mRepeatingLockButton )
1577  mRepeatingLockButton->setChecked( repeating );
1578 }
1579 
1581 {
1582  mRelative = relative;
1583  if ( mRelativeButton )
1584  {
1585  mRelativeButton->setChecked( relative );
1586  }
1587 }
1588 
1589 void QgsAdvancedDigitizingDockWidget::CadConstraint::setValue( double value, bool updateWidget )
1590 {
1591  mValue = value;
1592  if ( updateWidget && mLineEdit->isEnabled() )
1593  mLineEdit->setText( QLocale().toString( value, 'f', mPrecision ) );
1594 }
1595 
1597 {
1598  setLockMode( mLockMode == HardLock ? NoLock : HardLock );
1599 }
1600 
1602 {
1603  setRelative( !mRelative );
1604 }
1605 
1607 {
1608  mPrecision = precision;
1609  if ( mLineEdit->isEnabled() )
1610  mLineEdit->setText( QLocale().toString( mValue, 'f', mPrecision ) );
1611 }
1612 
1614 {
1615  if ( exist )
1616  *exist = pointsCount() > 0;
1617  if ( pointsCount() > 0 )
1618  return mCadPointList.value( 0 );
1619  else
1620  return QgsPoint();
1621 }
1622 
1624 {
1625  if ( pointsCount() > 0 && layer )
1626  {
1627  QgsPoint res = mCadPointList.value( 0 );
1628  const QgsPointXY layerCoordinates = mMapCanvas->mapSettings().mapToLayerCoordinates( layer, res );
1629  res.setX( layerCoordinates.x() );
1630  res.setY( layerCoordinates.y() );
1631  return res;
1632  }
1633  return QgsPoint();
1634 }
1635 
1637 {
1638  if ( exist )
1639  *exist = pointsCount() > 1;
1640  if ( pointsCount() > 1 )
1641  return mCadPointList.value( 1 );
1642  else
1643  return QgsPoint();
1644 }
1645 
1647 {
1648  if ( exist )
1649  *exist = pointsCount() > 2;
1650  if ( pointsCount() > 2 )
1651  return mCadPointList.value( 2 );
1652  else
1653  return QgsPoint();
1654 }
1655 
1656 QgsPoint QgsAdvancedDigitizingDockWidget::pointXYToPoint( const QgsPointXY &point ) const
1657 {
1658  return QgsPoint( point.x(), point.y(), getLineZ(), getLineM() );
1659 }
1660 
1662 {
1663  return mZLineEdit->isEnabled() ? QLocale().toDouble( mZLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
1664 }
1665 
1667 {
1668  return mMLineEdit->isEnabled() ? QLocale().toDouble( mMLineEdit->text() ) : std::numeric_limits<double>::quiet_NaN();
1669 }
The QgsAdvancedDigitizingCanvasItem class draws the graphical elements of the CAD tools (.
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:89
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:467
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
Added in 3.18.
@ MeshLayer
Added in 3.2.
@ VectorTileLayer
Added in 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
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.