QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
Go to the documentation of this file.
1 /***************************************************************************
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 *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15
16 #include <QSettings>
18
19 #include "math.h"
20
23 #include "qgsapplication.h"
24 #include "qgsexpression.h"
25 #include "qgslogger.h"
26 #include "qgsmapcanvas.h"
27 #include "qgsmaptoolcapture.h"
29 #include "qgsmessagebaritem.h"
30 #include "qgspoint.h"
31 #include "qgslinestringv2.h"
32 #include "qgsfocuswatcher.h"
33
35 {
36  bool acceptMatch( const QgsPointLocator::Match& m ) override { return m.hasEdge(); }
37 };
38
39 bool QgsAdvancedDigitizingDockWidget::lineCircleIntersection( const QgsPoint& center, const double radius, const QList<QgsPoint>& segment, QgsPoint& intersection )
40 {
41  Q_ASSERT( segment.count() == 2 );
42
43  // formula taken from http://mathworld.wolfram.com/Circle-LineIntersection.html
44
45  const double x1 = segment[0].x() - center.x();
46  const double y1 = segment[0].y() - center.y();
47  const double x2 = segment[1].x() - center.x();
48  const double y2 = segment[1].y() - center.y();
49  const double dx = x2 - x1;
50  const double dy = y2 - y1;
51
52  const double dr = sqrt( pow( dx, 2 ) + pow( dy, 2 ) );
53  const double d = x1 * y2 - x2 * y1;
54
55  const double disc = pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 );
56
57  if ( disc < 0 )
58  {
59  //no intersection or tangeant
60  return false;
61  }
62  else
63  {
64  // two solutions
65  const int sgnDy = dy < 0 ? -1 : 1;
66
67  const double ax = center.x() + ( d * dy + sgnDy * dx * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) );
68  const double ay = center.y() + ( -d * dx + qAbs( dy ) * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) );
69  const QgsPoint p1( ax, ay );
70
71  const double bx = center.x() + ( d * dy - sgnDy * dx * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) );
72  const double by = center.y() + ( -d * dx - qAbs( dy ) * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) );
73  const QgsPoint p2( bx, by );
74
75  // snap to nearest intersection
76
77  if ( intersection.sqrDist( p1 ) < intersection.sqrDist( p2 ) )
78  {
79  intersection.set( p1.x(), p1.y() );
80  }
81  else
82  {
83  intersection.set( p2.x(), p2.y() );
84  }
85  return true;
86  }
87 }
88
89
91  : QgsDockWidget( parent )
92  , mMapCanvas( canvas )
95  , mConstructionMode( false )
96  , mSnappingMode(( QgsMapMouseEvent::SnappingMode ) QSettings().value( "/Cad/SnappingMode", QgsMapMouseEvent::SnapProjectConfig ).toInt() )
97  , mCommonAngleConstraint( QSettings().value( "/Cad/CommonAngle", 90 ).toInt() )
98  , mSnappedToVertex( false )
99  , mSessionActive( false )
100  , mErrorMessage( nullptr )
101 {
102  setupUi( this );
103
105
106  mAngleConstraint.reset( new CadConstraint( mAngleLineEdit, mLockAngleButton, mRelativeAngleButton, mRepeatingLockAngleButton ) );
107  mDistanceConstraint.reset( new CadConstraint( mDistanceLineEdit, mLockDistanceButton, nullptr, mRepeatingLockDistanceButton ) );
108  mXConstraint.reset( new CadConstraint( mXLineEdit, mLockXButton, mRelativeXButton, mRepeatingLockXButton ) );
109  mYConstraint.reset( new CadConstraint( mYLineEdit, mLockYButton, mRelativeYButton, mRepeatingLockYButton ) );
111
112  mMapCanvas->installEventFilter( this );
113  mAngleLineEdit->installEventFilter( this );
114  mDistanceLineEdit->installEventFilter( this );
115  mXLineEdit->installEventFilter( this );
116  mYLineEdit->installEventFilter( this );
117
118  // this action is also used in the advanced digitizing tool bar
119  mEnableAction = new QAction( this );
120  mEnableAction->setText( tr( "Enable advanced digitizing tools" ) );
122  mEnableAction->setCheckable( true );
124  mEnabledButton->setDefaultAction( mEnableAction );
125
126  // Connect the UI to the event filter to update constraints
127  connect( mEnableAction, SIGNAL( triggered( bool ) ), this, SLOT( activateCad( bool ) ) );
128  connect( mConstructionModeButton, SIGNAL( clicked( bool ) ), this, SLOT( setConstructionMode( bool ) ) );
129  connect( mParallelButton, SIGNAL( clicked( bool ) ), this, SLOT( addtionalConstraintClicked( bool ) ) );
130  connect( mPerpendicularButton, SIGNAL( clicked( bool ) ), this, SLOT( addtionalConstraintClicked( bool ) ) );
131  connect( mLockAngleButton, SIGNAL( clicked( bool ) ), this, SLOT( lockConstraint( bool ) ) );
132  connect( mLockDistanceButton, SIGNAL( clicked( bool ) ), this, SLOT( lockConstraint( bool ) ) );
133  connect( mLockXButton, SIGNAL( clicked( bool ) ), this, SLOT( lockConstraint( bool ) ) );
134  connect( mLockYButton, SIGNAL( clicked( bool ) ), this, SLOT( lockConstraint( bool ) ) );
135  connect( mRelativeAngleButton, SIGNAL( clicked( bool ) ), this, SLOT( setConstraintRelative( bool ) ) );
136  connect( mRelativeXButton, SIGNAL( clicked( bool ) ), this, SLOT( setConstraintRelative( bool ) ) );
137  connect( mRelativeYButton, SIGNAL( clicked( bool ) ), this, SLOT( setConstraintRelative( bool ) ) );
138  connect( mRepeatingLockDistanceButton, SIGNAL( clicked( bool ) ), this, SLOT( setConstraintRepeatingLock( bool ) ) );
139  connect( mRepeatingLockAngleButton, SIGNAL( clicked( bool ) ), this, SLOT( setConstraintRepeatingLock( bool ) ) );
140  connect( mRepeatingLockXButton, SIGNAL( clicked( bool ) ), this, SLOT( setConstraintRepeatingLock( bool ) ) );
141  connect( mRepeatingLockYButton, SIGNAL( clicked( bool ) ), this, SLOT( setConstraintRepeatingLock( bool ) ) );
142  connect( mAngleLineEdit, SIGNAL( returnPressed() ), this, SLOT( lockConstraint() ) );
143  connect( mDistanceLineEdit, SIGNAL( returnPressed() ), this, SLOT( lockConstraint() ) );
144  connect( mXLineEdit, SIGNAL( returnPressed() ), this, SLOT( lockConstraint() ) );
145  connect( mYLineEdit, SIGNAL( returnPressed() ), this, SLOT( lockConstraint() ) );
146  connect( mAngleLineEdit, SIGNAL( textEdited( QString ) ), this, SLOT( constraintTextEdited( QString ) ) );
147  connect( mDistanceLineEdit, SIGNAL( textEdited( QString ) ), this, SLOT( constraintTextEdited( QString ) ) );
148  connect( mXLineEdit, SIGNAL( textEdited( QString ) ), this, SLOT( constraintTextEdited( QString ) ) );
149  connect( mYLineEdit, SIGNAL( textEdited( QString ) ), this, SLOT( constraintTextEdited( QString ) ) );
150  //also watch for focus out events on these widgets
151  QgsFocusWatcher* angleWatcher = new QgsFocusWatcher( mAngleLineEdit );
152  connect( angleWatcher, SIGNAL( focusOut() ), this, SLOT( constraintFocusOut() ) );
153  QgsFocusWatcher* distanceWatcher = new QgsFocusWatcher( mDistanceLineEdit );
154  connect( distanceWatcher, SIGNAL( focusOut() ), this, SLOT( constraintFocusOut() ) );
155  QgsFocusWatcher* xWatcher = new QgsFocusWatcher( mXLineEdit );
156  connect( xWatcher, SIGNAL( focusOut() ), this, SLOT( constraintFocusOut() ) );
157  QgsFocusWatcher* yWatcher = new QgsFocusWatcher( mYLineEdit );
158  connect( yWatcher, SIGNAL( focusOut() ), this, SLOT( constraintFocusOut() ) );
159
162  // common angles
163  QActionGroup* angleButtonGroup = new QActionGroup( menu ); // actions are exclusive for common angles
164  mCommonAngleActions = QMap<QAction*, int>();
165  QList< QPair< int, QString > > commonAngles;
166  commonAngles << QPair<int, QString>( 0, tr( "Do not snap to common angles" ) );
167  commonAngles << QPair<int, QString>( 30, tr( "Snap to 30%1 angles" ).arg( QString::fromUtf8( "°" ) ) );
168  commonAngles << QPair<int, QString>( 45, tr( "Snap to 45%1 angles" ).arg( QString::fromUtf8( "°" ) ) );
169  commonAngles << QPair<int, QString>( 90, tr( "Snap to 90%1 angles" ).arg( QString::fromUtf8( "°" ) ) );
170  for ( QList< QPair< int, QString > >::const_iterator it = commonAngles.begin(); it != commonAngles.end(); ++it )
171  {
172  QAction* action = new QAction( it->second, menu );
173  action->setCheckable( true );
174  action->setChecked( it->first == mCommonAngleConstraint );
177  mCommonAngleActions.insert( action, it->first );
178  }
179  // snapping on layers
181  QActionGroup* snapButtonGroup = new QActionGroup( menu ); // actions are exclusive for snapping modes
184  snappingModes << QPair<QgsMapMouseEvent::SnappingMode, QString>( QgsMapMouseEvent::NoSnapping, tr( "Do not snap to vertices or segment" ) );
185  snappingModes << QPair<QgsMapMouseEvent::SnappingMode, QString>( QgsMapMouseEvent::SnapProjectConfig, tr( "Snap according to project configuration" ) );
186  snappingModes << QPair<QgsMapMouseEvent::SnappingMode, QString>( QgsMapMouseEvent::SnapAllLayers, tr( "Snap to all layers" ) );
187  for ( QList< QPair< QgsMapMouseEvent::SnappingMode, QString > >::const_iterator it = snappingModes.begin(); it != snappingModes.end(); ++it )
188  {
189  QAction* action = new QAction( it->second, menu );
190  action->setCheckable( true );
191  action->setChecked( it->first == mSnappingMode );
194  mSnappingActions.insert( action, it->first );
195  }
196
198  connect( mSettingsButton, SIGNAL( triggered( QAction* ) ), this, SLOT( settingsButtonTriggered( QAction* ) ) );
199
200  updateCapacity( true );
201  disable();
202 }
203
205 {
206  // disable CAD but do not unset map event filter
207  // so it will be reactivated whenever the map tool is show again
209 }
210
212 {
214  mEnableAction->setChecked( enabled );
216  mInputWidgets->setEnabled( enabled );
217
218  clearPoints();
219  releaseLocks();
220  setConstructionMode( false );
221 }
222
224 {
226
227  mSessionActive = enabled;
228
229  if ( enabled && !isVisible() )
230  {
231  show();
232  }
233
235 }
236
238 {
239  if ( !activated )
240  {
242  }
243  if ( sender() == mParallelButton )
244  {
246  }
247  else if ( sender() == mPerpendicularButton )
248  {
250  }
251 }
252
253
254 void QgsAdvancedDigitizingDockWidget::setConstraintRelative( bool activate )
255 {
256  if ( sender() == mRelativeAngleButton )
257  {
258  mAngleConstraint->setRelative( activate );
259  }
260  else if ( sender() == mRelativeXButton )
261  {
262  mXConstraint->setRelative( activate );
263  }
264  else if ( sender() == mRelativeYButton )
265  {
266  mYConstraint->setRelative( activate );
267  }
268 }
269
270 void QgsAdvancedDigitizingDockWidget::setConstraintRepeatingLock( bool activate )
271 {
272  if ( sender() == mRepeatingLockDistanceButton )
273  {
274  mDistanceConstraint->setRepeatingLock( activate );
275  }
276  else if ( sender() == mRepeatingLockAngleButton )
277  {
278  mAngleConstraint->setRepeatingLock( activate );
279  }
280  else if ( sender() == mRepeatingLockXButton )
281  {
282  mXConstraint->setRepeatingLock( activate );
283  }
284  else if ( sender() == mRepeatingLockYButton )
285  {
286  mYConstraint->setRepeatingLock( activate );
287  }
288 }
289
290 void QgsAdvancedDigitizingDockWidget::setConstructionMode( bool enabled )
291 {
292  mConstructionMode = enabled;
293  mConstructionModeButton->setChecked( enabled );
294 }
295
296 void QgsAdvancedDigitizingDockWidget::settingsButtonTriggered( QAction* action )
297 {
298  // snapping
300  if ( isn != mSnappingActions.constEnd() )
301  {
302  isn.key()->setChecked( true );
303  mSnappingMode = isn.value();
304  QSettings().setValue( "/Cad/SnappingMode", ( int )isn.value() );
305  return;
306  }
307
308  // common angles
309  QMap<QAction*, int>::const_iterator ica = mCommonAngleActions.constFind( action );
310  if ( ica != mCommonAngleActions.constEnd() )
311  {
312  ica.key()->setChecked( true );
313  mCommonAngleConstraint = ica.value();
315  return;
316  }
317 }
318
319 void QgsAdvancedDigitizingDockWidget::releaseLocks( bool releaseRepeatingLocks )
320 {
321  // release all locks except construction mode
322
324
325  if ( releaseRepeatingLocks || !mAngleConstraint->isRepeatingLock() )
327  if ( releaseRepeatingLocks || !mDistanceConstraint->isRepeatingLock() )
329  if ( releaseRepeatingLocks || !mXConstraint->isRepeatingLock() )
331  if ( releaseRepeatingLocks || !mYConstraint->isRepeatingLock() )
333 }
334
335 #if 0
337 {
338  // run a fake map mouse event to update the paint item
339  QPoint globalPos = mMapCanvas->cursor().pos();
340  QPoint pos = mMapCanvas->mapFromGlobal( globalPos );
341  QMouseEvent* e = new QMouseEvent( QEvent::MouseMove, pos, globalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier );
342  mCurrentMapTool->canvasMoveEvent( e );
343 }
344 #endif
345
347 {
349  if ( obj == mAngleLineEdit || obj == mLockAngleButton )
350  {
351  constraint = mAngleConstraint.data();
352  }
353  else if ( obj == mDistanceLineEdit || obj == mLockDistanceButton )
354  {
355  constraint = mDistanceConstraint.data();
356  }
357  else if ( obj == mXLineEdit || obj == mLockXButton )
358  {
359  constraint = mXConstraint.data();
360  }
361  else if ( obj == mYLineEdit || obj == mLockYButton )
362  {
363  constraint = mYConstraint.data();
364  }
365  return constraint;
366 }
367
368 double QgsAdvancedDigitizingDockWidget::parseUserInput( const QString& inputValue, bool& ok ) const
369 {
370  ok = false;
371  double value = inputValue.toDouble( &ok );
372  if ( ok )
373  {
374  return value;
375  }
376  else
377  {
378  // try to evalute expression
379  QgsExpression expr( inputValue );
380  QVariant result = expr.evaluate();
381  if ( expr.hasEvalError() )
382  ok = false;
383  else
384  value = result.toDouble( &ok );
385  return value;
386  }
387 }
388
390 {
391  if ( !constraint || textValue.isEmpty() )
392  {
393  return;
394  }
395
396  if ( constraint->lockMode() == CadConstraint::NoLock )
397  return;
398
399  bool ok;
400  double value = parseUserInput( textValue, ok );
401  if ( !ok )
402  return;
403
404  constraint->setValue( value, convertExpression );
405  // run a fake map mouse event to update the paint item
406  emit pointChanged( mCadPointList.value( 0 ) );
407 }
408
409 void QgsAdvancedDigitizingDockWidget::lockConstraint( bool activate /* default true */ )
410 {
411  CadConstraint* constraint = objectToConstraint( sender() );
412  if ( !constraint )
413  {
414  return;
415  }
416
417  if ( activate )
418  {
419  QString textValue = constraint->lineEdit()->text();
420  if ( !textValue.isEmpty() )
421  {
422  bool ok;
423  double value = parseUserInput( textValue, ok );
424  if ( ok )
425  {
426  constraint->setValue( value );
427  }
428  else
429  {
430  activate = false;
431  }
432  }
433  else
434  {
435  activate = false;
436  }
437  }
439
440  if ( activate )
441  {
442  // deactivate perpendicular/parallel if angle has been activated
443  if ( constraint == mAngleConstraint.data() )
444  {
446  }
447
448  // run a fake map mouse event to update the paint item
449  emit pointChanged( mCadPointList.value( 0 ) );
450  }
451 }
452
453 void QgsAdvancedDigitizingDockWidget::constraintTextEdited( const QString& textValue )
454 {
455  CadConstraint* constraint = objectToConstraint( sender() );
456  if ( !constraint )
457  {
458  return;
459  }
460
461  updateConstraintValue( constraint, textValue, false );
462 }
463
465 {
466  QLineEdit* lineEdit = qobject_cast< QLineEdit* >( sender()->parent() );
467  if ( !lineEdit )
468  return;
469
470  CadConstraint* constraint = objectToConstraint( lineEdit );
471  if ( !constraint )
472  {
473  return;
474  }
475
476  updateConstraintValue( constraint, lineEdit->text(), true );
477 }
478
480 {
482  mPerpendicularButton->setChecked( constraint == Perpendicular );
483  mParallelButton->setChecked( constraint == Parallel );
484 }
485
486 void QgsAdvancedDigitizingDockWidget::updateCapacity( bool updateUIwithoutChange )
487 {
489  // first point is the mouse point (it doesn't count)
490  if ( mCadPointList.count() > 1 )
491  {
492  newCapacities |= AbsoluteAngle | RelativeCoordinates;
493  }
494  if ( mCadPointList.count() > 2 )
495  {
496  newCapacities |= RelativeAngle;
497  }
498  if ( !updateUIwithoutChange && newCapacities == mCapacities )
499  {
500  return;
501  }
502
503  // update the UI according to new capacities
504  // still keep the old to compare
505
506  bool relativeAngle = mCadEnabled && newCapacities.testFlag( RelativeAngle );
507  bool absoluteAngle = mCadEnabled && newCapacities.testFlag( AbsoluteAngle );
508  bool relativeCoordinates = mCadEnabled && newCapacities.testFlag( RelativeCoordinates );
509
510  mPerpendicularButton->setEnabled( absoluteAngle );
511  mParallelButton->setEnabled( absoluteAngle );
512  if ( !absoluteAngle )
513  {
515  }
516
517  // absolute angle = azimuth, relative = from previous line
518  mLockAngleButton->setEnabled( absoluteAngle );
519  mRelativeAngleButton->setEnabled( relativeAngle );
520  mAngleLineEdit->setEnabled( absoluteAngle );
521  if ( !absoluteAngle )
522  {
524  }
525  if ( !relativeAngle )
526  {
527  mAngleConstraint->setRelative( false );
528  }
529  else if ( relativeAngle && !mCapacities.testFlag( RelativeAngle ) )
530  {
531  // set angle mode to relative if can do and wasn't available before
532  mAngleConstraint->setRelative( true );
533  }
534
535  // distance is alway relative
536  mLockDistanceButton->setEnabled( relativeCoordinates );
537  mDistanceLineEdit->setEnabled( relativeCoordinates );
538  if ( !relativeCoordinates )
539  {
541  }
542
543  mRelativeXButton->setEnabled( relativeCoordinates );
544  mRelativeYButton->setEnabled( relativeCoordinates );
545
546  // update capacities
547  mCapacities = newCapacities;
548 }
549
550
552 {
553  bool res = true;
554
555  QgsDebugMsg( "Constraints (locked / relative / value" );
556  QgsDebugMsg( QString( "Angle: %1 %2 %3" ).arg( mAngleConstraint->isLocked() ).arg( mAngleConstraint->relative() ).arg( mAngleConstraint->value() ) );
557  QgsDebugMsg( QString( "Distance: %1 %2 %3" ).arg( mDistanceConstraint->isLocked() ).arg( mDistanceConstraint->relative() ).arg( mDistanceConstraint->value() ) );
558  QgsDebugMsg( QString( "X: %1 %2 %3" ).arg( mXConstraint->isLocked() ).arg( mXConstraint->relative() ).arg( mXConstraint->value() ) );
559  QgsDebugMsg( QString( "Y: %1 %2 %3" ).arg( mYConstraint->isLocked() ).arg( mYConstraint->relative() ).arg( mYConstraint->value() ) );
560
561  QgsPoint point = e->snapPoint( mSnappingMode );
562
563  mSnappedSegment = e->snapSegment( mSnappingMode );
564
565  bool previousPointExist, penulPointExist;
566  QgsPoint previousPt = previousPoint( &previousPointExist );
567  QgsPoint penultimatePt = penultimatePoint( &penulPointExist );
568
569  // *****************************
570  // ---- X Constrain
571  if ( mXConstraint->isLocked() )
572  {
573  if ( !mXConstraint->relative() )
574  {
575  point.setX( mXConstraint->value() );
576  }
577  else if ( mCapacities.testFlag( RelativeCoordinates ) )
578  {
579  point.setX( previousPt.x() + mXConstraint->value() );
580  }
581  if ( !mSnappedSegment.isEmpty() && !mYConstraint->isLocked() )
582  {
583  // intersect with snapped segment line at X ccordinate
584  const double dx = mSnappedSegment.at( 1 ).x() - mSnappedSegment.at( 0 ).x();
585  if ( dx == 0 )
586  {
587  point.setY( mSnappedSegment.at( 0 ).y() );
588  }
589  else
590  {
591  const double dy = mSnappedSegment.at( 1 ).y() - mSnappedSegment.at( 0 ).y();
592  point.setY( mSnappedSegment.at( 0 ).y() + ( dy * ( point.x() - mSnappedSegment.at( 0 ).x() ) ) / dx );
593  }
594  }
595  }
596  // *****************************
597  // ---- Y Constrain
598  if ( mYConstraint->isLocked() )
599  {
600  if ( !mYConstraint->relative() )
601  {
602  point.setY( mYConstraint->value() );
603  }
604  else if ( mCapacities.testFlag( RelativeCoordinates ) )
605  {
606  point.setY( previousPt.y() + mYConstraint->value() );
607  }
608  if ( !mSnappedSegment.isEmpty() && !mXConstraint->isLocked() )
609  {
610  // intersect with snapped segment line at Y ccordinate
611  const double dy = mSnappedSegment.at( 1 ).y() - mSnappedSegment.at( 0 ).y();
612  if ( dy == 0 )
613  {
614  point.setX( mSnappedSegment.at( 0 ).x() );
615  }
616  else
617  {
618  const double dx = mSnappedSegment.at( 1 ).x() - mSnappedSegment.at( 0 ).x();
619  point.setX( mSnappedSegment.at( 0 ).x() + ( dx * ( point.y() - mSnappedSegment.at( 0 ).y() ) ) / dy );
620  }
621  }
622  }
623  // *****************************
624  // ---- Angle constrain
625  // input angles are in degrees
626  if ( mAngleConstraint->lockMode() == CadConstraint::SoftLock )
627  {
628  // reset the lock
630  }
631  if ( !mAngleConstraint->isLocked() && mCapacities.testFlag( AbsoluteAngle ) && mCommonAngleConstraint != 0 )
632  {
633  double commonAngle = mCommonAngleConstraint * M_PI / 180;
634  // see if soft common angle constraint should be performed
635  // only if not in HardLock mode
636  double softAngle = qAtan2( point.y() - previousPt.y(),
637  point.x() - previousPt.x() );
638  double deltaAngle = 0;
639  if ( mAngleConstraint->relative() && mCapacities.testFlag( RelativeAngle ) )
640  {
641  // compute the angle relative to the last segment (0° is aligned with last segment)
642  deltaAngle = qAtan2( previousPt.y() - penultimatePt.y(),
643  previousPt.x() - penultimatePt.x() );
644  softAngle -= deltaAngle;
645  }
646  int quo = qRound( softAngle / commonAngle );
647  if ( qAbs( softAngle - quo * commonAngle ) * 180.0 * M_1_PI <= SoftConstraintToleranceDegrees )
648  {
649  // also check the distance in pixel to the line, otherwise it's too sticky at long ranges
650  softAngle = quo * commonAngle ;
651  // http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html
652  // use the direction vector (cos(a),sin(a)) from previous point. |x2-x1|=1 since sin2+cos2=1
653  const double dist = qAbs( qCos( softAngle + deltaAngle ) * ( previousPt.y() - point.y() )
654  - qSin( softAngle + deltaAngle ) * ( previousPt.x() - point.x() ) );
655  if ( dist / mMapCanvas->mapSettings().mapUnitsPerPixel() < SoftConstraintTolerancePixel )
656  {
658  mAngleConstraint->setValue( 180.0 / M_PI * softAngle );
659  }
660  }
661  }
662  if ( mAngleConstraint->isLocked() )
663  {
664  double angleValue = mAngleConstraint->value() * M_PI / 180;
665  if ( mAngleConstraint->relative() && mCapacities.testFlag( RelativeAngle ) )
666  {
667  // compute the angle relative to the last segment (0° is aligned with last segment)
668  angleValue += qAtan2( previousPt.y() - penultimatePt.y(),
669  previousPt.x() - penultimatePt.x() );
670  }
671
672  double cosa = qCos( angleValue );
673  double sina = qSin( angleValue );
674  double v = ( point.x() - previousPt.x() ) * cosa + ( point.y() - previousPt.y() ) * sina ;
675  if ( mXConstraint->isLocked() && mYConstraint->isLocked() )
676  {
677  // do nothing if both X,Y are already locked
678  }
679  else if ( mXConstraint->isLocked() )
680  {
681  if ( qgsDoubleNear( cosa, 0.0 ) )
682  {
683  res = false;
684  }
685  else
686  {
687  double x = mXConstraint->value();
688  if ( !mXConstraint->relative() )
689  {
690  x -= previousPt.x();
691  }
692  point.setY( previousPt.y() + x * sina / cosa );
693  }
694  }
695  else if ( mYConstraint->isLocked() )
696  {
697  if ( qgsDoubleNear( sina, 0.0 ) )
698  {
699  res = false;
700  }
701  else
702  {
703  double y = mYConstraint->value();
704  if ( !mYConstraint->relative() )
705  {
706  y -= previousPt.y();
707  }
708  point.setX( previousPt.x() + y * cosa / sina );
709  }
710  }
711  else
712  {
713  point.setX( previousPt.x() + cosa * v );
714  point.setY( previousPt.y() + sina * v );
715  }
716
717  if ( !mSnappedSegment.isEmpty() && !mDistanceConstraint->isLocked() )
718  {
719  // magnetize to the intersection of the snapped segment and the lockedAngle
720
721  // line of previous point + locked angle
722  const double x1 = previousPt.x();
723  const double y1 = previousPt.y();
724  const double x2 = previousPt.x() + cosa;
725  const double y2 = previousPt.y() + sina;
726  // line of snapped segment
727  const double x3 = mSnappedSegment.at( 0 ).x();
728  const double y3 = mSnappedSegment.at( 0 ).y();
729  const double x4 = mSnappedSegment.at( 1 ).x();
730  const double y4 = mSnappedSegment.at( 1 ).y();
731
732  const double d = ( x1 - x2 ) * ( y3 - y4 ) - ( y1 - y2 ) * ( x3 - x4 );
733
734  // do not compute intersection if lines are almost parallel
735  // this threshold might be adapted
736  if ( qAbs( d ) > 0.01 )
737  {
738  point.setX((( x3 - x4 )*( x1*y2 - y1*x2 ) - ( x1 - x2 )*( x3*y4 - y3*x4 ) ) / d );
739  point.setY((( y3 - y4 )*( x1*y2 - y1*x2 ) - ( y1 - y2 )*( x3*y4 - y3*x4 ) ) / d );
740  }
741  }
742  }
743  // *****************************
744  // ---- Distance constraint
745  if ( mDistanceConstraint->isLocked() && previousPointExist )
746  {
747  if ( mXConstraint->isLocked() || mYConstraint->isLocked() )
748  {
749  // perform both to detect errors in constraints
750  if ( mXConstraint->isLocked() )
751  {
752  const QList<QgsPoint> verticalSegment = QList<QgsPoint>()
753  << QgsPoint( mXConstraint->value(), point.y() )
754  << QgsPoint( mXConstraint->value(), point.y() + 1 );
755  res &= lineCircleIntersection( previousPt, mDistanceConstraint->value(), verticalSegment, point );
756  }
757  if ( mYConstraint->isLocked() )
758  {
759  const QList<QgsPoint> horizontalSegment = QList<QgsPoint>()
760  << QgsPoint( point.x(), mYConstraint->value() )
761  << QgsPoint( point.x() + 1, mYConstraint->value() );
762  res &= lineCircleIntersection( previousPt, mDistanceConstraint->value(), horizontalSegment, point );
763  }
764  }
765  else
766  {
767  const double dist = sqrt( point.sqrDist( previousPt ) );
768  if ( dist == 0 )
769  {
770  // handle case where mouse is over origin and distance constraint is enabled
771  // take arbitrary horizontal line
772  point.set( previousPt.x() + mDistanceConstraint->value(), previousPt.y() );
773  }
774  else
775  {
776  const double vP = mDistanceConstraint->value() / dist;
777  point.set( previousPt.x() + ( point.x() - previousPt.x() ) * vP,
778  previousPt.y() + ( point.y() - previousPt.y() ) * vP );
779  }
780
781  if ( !mSnappedSegment.isEmpty() && !mAngleConstraint->isLocked() )
782  {
783  // we will magnietize to the intersection of that segment and the lockedDistance !
784  res &= lineCircleIntersection( previousPt, mDistanceConstraint->value(), snappedSegment(), point );
785  }
786  }
787  }
788
789  // *****************************
790  // ---- caluclate CAD values
791  QgsDebugMsg( QString( "point: %1 %2" ).arg( point.x() ).arg( point.y() ) );
792  QgsDebugMsg( QString( "previous point: %1 %2" ).arg( previousPt.x() ).arg( previousPt.y() ) );
793  QgsDebugMsg( QString( "penultimate point: %1 %2" ).arg( penultimatePt.x() ).arg( penultimatePt.y() ) );
794  //QgsDebugMsg( QString( "dx: %1 dy: %2" ).arg( point.x() - previousPt.x() ).arg( point.y() - previousPt.y() ) );
795  //QgsDebugMsg( QString( "ddx: %1 ddy: %2" ).arg( previousPt.x() - penultimatePt.x() ).arg( previousPt.y() - penultimatePt.y() ) );
796
797  // set the point coordinates in the map event
798  e->setMapPoint( point );
799
800  // update the point list
801  updateCurrentPoint( point );
802
803  // *****************************
804  // ---- update the GUI with the values
805  // --- angle
806  if ( !mAngleConstraint->isLocked() && previousPointExist )
807  {
808  double angle = 0.0;
809  if ( penulPointExist && mAngleConstraint->relative() )
810  {
811  // previous angle
812  angle = qAtan2( previousPt.y() - penultimatePt.y(),
813  previousPt.x() - penultimatePt.x() );
814  }
815  angle = ( qAtan2( point.y() - previousPt.y(),
816  point.x() - previousPt.x()
817  ) - angle ) * 180 / M_PI;
818  // modulus
819  angle = fmod( angle, 360.0 );
820  mAngleConstraint->setValue( angle );
821  }
822  // --- distance
823  if ( !mDistanceConstraint->isLocked() && previousPointExist )
824  {
825  mDistanceConstraint->setValue( sqrt( previousPt.sqrDist( point ) ) );
826  }
827  // --- X
828  if ( !mXConstraint->isLocked() )
829  {
830  if ( previousPointExist && mXConstraint->relative() )
831  {
832  mXConstraint->setValue( point.x() - previousPt.x() );
833  }
834  else
835  {
836  mXConstraint->setValue( point.x() );
837  }
838  }
839  // --- Y
840  if ( !mYConstraint->isLocked() )
841  {
842  if ( previousPointExist && mYConstraint->relative() )
843  {
844  mYConstraint->setValue( point.y() - previousPt.y() );
845  }
846  else
847  {
848  mYConstraint->setValue( point.y() );
849  }
850  }
851
852  return res;
853 }
854
855
857 {
858  if ( mAdditionalConstraint == NoConstraint )
859  {
860  return false;
861  }
862
863  bool previousPointExist, penulPointExist, mSnappedSegmentExist;
864  QgsPoint previousPt = previousPoint( &previousPointExist );
865  QgsPoint penultimatePt = penultimatePoint( &penulPointExist );
866  QList<QgsPoint> mSnappedSegment = e->snapSegment( mSnappingMode, &mSnappedSegmentExist, true );
867
868  if ( !previousPointExist || !mSnappedSegmentExist )
869  {
870  return false;
871  }
872
873  double angle = qAtan2( mSnappedSegment[0].y() - mSnappedSegment[1].y(), mSnappedSegment[0].x() - mSnappedSegment[1].x() );
874
875  if ( mAngleConstraint->relative() && penulPointExist )
876  {
877  angle -= qAtan2( previousPt.y() - penultimatePt.y(), previousPt.x() - penultimatePt.x() );
878  }
879
880  if ( mAdditionalConstraint == Perpendicular )
881  {
882  angle += M_PI_2;
883  }
884
885  angle *= 180 / M_PI;
886
887  mAngleConstraint->setValue( angle );
888  mAngleConstraint->setLockMode( lockMode );
889  if ( lockMode == CadConstraint::HardLock )
890  {
892  }
893
894  return true;
895 }
896
898 {
899  applyConstraints( e );
901 }
902
904 {
906  return false;
907
908  emit popWarning();
909
910  if ( e->button() == Qt::RightButton )
911  {
912  clearPoints();
913  releaseLocks();
914  return false;
915  }
916
917  applyConstraints( e );
918
919  if ( alignToSegment( e ) )
920  {
921  // launch a fake move event so rubber bands of map tools will be adapted with new constraints
922  // emit pointChanged( e );
923
924  // Parallel or perpendicular mode and snapped to segment
925  // this has emitted the lockAngle signal
926  return true;
927  }
928
930
931  releaseLocks( false );
932
933  if ( e->button() == Qt::LeftButton )
934  {
935  // stop digitizing if not intermediate point and if line or polygon
936  if ( !mConstructionMode && !captureSegment )
937  {
938  clearPoints();
939  }
940  }
941  return mConstructionMode;
942 }
943
945 {
947  return false;
948
949  if ( !applyConstraints( e ) )
950  {
951  emit pushWarning( tr( "Some constraints are incompatible. Resulting point might be incorrect." ) );
952  }
953  else
954  {
955  popWarning();
956  }
957
958  // perpendicular/parallel constraint
959  // do a soft lock when snapping to a segment
962
963  return false;
964 }
965
967 {
968  // event on map tool
969
971  return false;
972
973  switch ( e->key() )
974  {
975  case Qt::Key_Backspace:
976  case Qt::Key_Delete:
977  {
978  removePreviousPoint();
979  releaseLocks( false );
980  break;
981  }
982  case Qt::Key_Escape:
983  {
984  releaseLocks();
985  break;
986  }
987  default:
988  {
989  keyPressEvent( e );
990  break;
991  }
992  }
993  // for map tools, continues with key press in any case
994  return false;
995 }
996
998 {
999  clearPoints();
1000  releaseLocks();
1001 }
1002
1004 {
1005  // event on dock (this)
1006
1008  return;
1009
1010  switch ( e->key() )
1011  {
1012  case Qt::Key_Backspace:
1013  case Qt::Key_Delete:
1014  {
1015  removePreviousPoint();
1016  releaseLocks( false );
1017  break;
1018  }
1019  case Qt::Key_Escape:
1020  {
1021  releaseLocks();
1022  break;
1023  }
1024  default:
1025  {
1026  filterKeyPress( e );
1027  break;
1028  }
1029  }
1030 }
1031
1032 bool QgsAdvancedDigitizingDockWidget::eventFilter( QObject* obj, QEvent* event )
1033 {
1034  // event for line edits
1035  Q_UNUSED( obj );
1036  if ( event->type() != QEvent::KeyPress )
1037  {
1038  return false;
1039  }
1040  QKeyEvent* keyEvent = dynamic_cast<QKeyEvent*>( event );
1041  if ( !keyEvent )
1042  {
1043  return false;
1044  }
1045  return filterKeyPress( keyEvent ) ;
1046 }
1047
1048 bool QgsAdvancedDigitizingDockWidget::filterKeyPress( QKeyEvent* e )
1049 {
1050  switch ( e->key() )
1051  {
1052  case Qt::Key_X:
1053  {
1054  if ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier )
1055  {
1056  mXConstraint->toggleLocked();
1057  emit pointChanged( mCadPointList.value( 0 ) );
1058  }
1059  else if ( e->modifiers() == Qt::ShiftModifier )
1060  {
1061  if ( mCapacities.testFlag( RelativeCoordinates ) )
1062  {
1063  mXConstraint->toggleRelative();
1064  emit pointChanged( mCadPointList.value( 0 ) );
1065  }
1066  }
1067  else
1068  {
1069  mXLineEdit->setFocus();
1070  mXLineEdit->selectAll();
1071  }
1072  break;
1073  }
1074  case Qt::Key_Y:
1075  {
1076  if ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier )
1077  {
1078  mYConstraint->toggleLocked();
1079  emit pointChanged( mCadPointList.value( 0 ) );
1080  }
1081  else if ( e->modifiers() == Qt::ShiftModifier )
1082  {
1083  if ( mCapacities.testFlag( RelativeCoordinates ) )
1084  {
1085  mYConstraint->toggleRelative();
1086  emit pointChanged( mCadPointList.value( 0 ) );
1087  }
1088  }
1089  else
1090  {
1091  mYLineEdit->setFocus();
1092  mYLineEdit->selectAll();
1093  }
1094  break;
1095  }
1096  case Qt::Key_A:
1097  {
1098  if ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier )
1099  {
1100  if ( mCapacities.testFlag( AbsoluteAngle ) )
1101  {
1102  mAngleConstraint->toggleLocked();
1103  emit pointChanged( mCadPointList.value( 0 ) );
1104  }
1105  }
1106  else if ( e->modifiers() == Qt::ShiftModifier )
1107  {
1108  if ( mCapacities.testFlag( RelativeAngle ) )
1109  {
1110  mAngleConstraint->toggleRelative();
1111  emit pointChanged( mCadPointList.value( 0 ) );
1112  }
1113  }
1114  else
1115  {
1116  mAngleLineEdit->setFocus();
1117  mAngleLineEdit->selectAll();
1118  }
1119  break;
1120  }
1121  case Qt::Key_D:
1122  {
1123  if ( e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ControlModifier )
1124  {
1125  if ( mCapacities.testFlag( RelativeCoordinates ) )
1126  {
1127  mDistanceConstraint->toggleLocked();
1128  emit pointChanged( mCadPointList.value( 0 ) );
1129  }
1130  }
1131  else
1132  {
1133  mDistanceLineEdit->setFocus();
1134  mDistanceLineEdit->selectAll();
1135  }
1136  break;
1137  }
1138  case Qt::Key_C:
1139  {
1140  setConstructionMode( !mConstructionMode );
1141  break;
1142  }
1143  case Qt::Key_P:
1144  {
1145  bool parallel = mParallelButton->isChecked();
1146  bool perpendicular = mPerpendicularButton->isChecked();
1147
1148  if ( !parallel && !perpendicular )
1149  {
1151  }
1152  else if ( perpendicular )
1153  {
1155  }
1156  else
1157  {
1159  }
1160  break;
1161  }
1162  default:
1163  {
1164  return false; // continues
1165  }
1166  }
1167  return true; // stop the event
1168 }
1169
1171 {
1172  if ( mMapCanvas->mapSettings().destinationCrs().geographicFlag() )
1173  {
1174  mErrorLabel->setText( tr( "CAD tools can not be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1175  mErrorLabel->show();
1176  mEnableAction->setEnabled( false );
1178  }
1179  else
1180  {
1181  mEnableAction->setEnabled( true );
1182  mErrorLabel->hide();
1184  setMaximumHeight( 220 );
1185
1187
1188  if ( mSessionActive && !isVisible() )
1189  {
1190  show();
1191  }
1193  }
1194 }
1195
1197 {
1198  mEnableAction->setEnabled( false );
1199  mErrorLabel->setText( tr( "CAD tools are not enabled for the current map tool" ) );
1200  mErrorLabel->show();
1202  setMaximumHeight( 80 );
1203
1205
1207 }
1208
1210 {
1211  if ( !pointsCount() )
1212  {
1214  }
1215  else
1216  {
1218  }
1219
1220  updateCapacity();
1221 }
1222
1224 {
1225  if ( !pointsCount() )
1226  return;
1227
1228  int i = pointsCount() > 1 ? 1 : 0;
1230  updateCapacity();
1231 }
1232
1234 {
1236  mSnappedSegment.clear();
1237  mSnappedToVertex = false;
1238
1239  updateCapacity();
1240 }
1241
1242 void QgsAdvancedDigitizingDockWidget::updateCurrentPoint( const QgsPoint& point )
1243 {
1244  if ( !pointsCount() )
1245  {
1247  updateCapacity();
1248  }
1249  else
1250  {
1252  }
1253 }
1254
1255
1257 {
1258  mLockMode = mode;
1259  mLockerButton->setChecked( mode == HardLock );
1260  if ( mRepeatingLockButton )
1261  {
1262  if ( mode == HardLock )
1263  {
1264  mRepeatingLockButton->setEnabled( true );
1265  }
1266  else
1267  {
1268  mRepeatingLockButton->setChecked( false );
1269  mRepeatingLockButton->setEnabled( false );
1270  }
1271  }
1272
1273  if ( mode == NoLock )
1274  {
1275  mLineEdit->clear();
1276  }
1277 }
1278
1280 {
1281  mRepeatingLock = repeating;
1282  if ( mRepeatingLockButton )
1283  mRepeatingLockButton->setChecked( repeating );
1284 }
1285
1287 {
1288  mRelative = relative;
1289  if ( mRelativeButton )
1290  {
1291  mRelativeButton->setChecked( relative );
1292  }
1293 }
1294
1296 {
1297  mValue = value;
1298  if ( updateWidget )
1299  mLineEdit->setText( QString::number( value, 'f' ) );
1300 }
1301
1303 {
1304  setLockMode( mLockMode == HardLock ? NoLock : HardLock );
1305 }
1306
1308 {
1309  setRelative( mRelative ? false : true );
1310 }
1311
1313 {
1314  if ( exist )
1315  *exist = pointsCount() > 0;
1316  if ( pointsCount() > 0 )
1318  else
1319  return QgsPoint();
1320 }
1321
1323 {
1324  if ( exist )
1325  *exist = pointsCount() > 1;
1326  if ( pointsCount() > 1 )
1328  else
1329  return QgsPoint();
1330 }
1331
1333 {
1334  if ( exist )
1335  *exist = pointsCount() > 2;
1336  if ( pointsCount() > 2 )
1338  else
1339  return QgsPoint();
1340 }
void setText(const QString &text)
int pointsCount() const
The number of points in the CAD point helper list.
this corresponds to distance and relative coordinates
Class for parsing and evaluation of expressions (formerly called "search strings").
const QList< QgsPoint > & snappedSegment() const
Snapped to a segment.
void clear()
The CadConstraint is an abstract class for all basic constraints (angle/distance/x/y).
Qt::KeyboardModifiers modifiers() const
Type type() const
void setupUi(QWidget *widget)
static const double SoftConstraintToleranceDegrees
A event filter for watching for focus events on a parent object.
Q_DECL_DEPRECATED QVariant evaluate(const QgsFeature *f)
Evaluate the feature and return the result.
snap to all rendered layers (tolerance and type from defaultSettings())
QgsPoint mapPoint() const
mapPoint returns the point in coordinates
bool canvasReleaseEvent(QgsMapMouseEvent *e, bool captureSegment)
Will react on a canvas release event.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
QObject * sender() const
void setChecked(bool)
bool acceptMatch(const QgsPointLocator::Match &m) override
static QIcon getThemeIcon(const QString &theName)
Helper to get a theme icon.
void setIcon(const QIcon &icon)
const T & at(int i) const
void removeAt(int i)
int y() const
bool isVisible() const
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
const_iterator constFind(const Key &key) const
virtual bool applyConstraints(QgsMapMouseEvent *e)
void setRelative(bool relative)
Set if the constraint should be treated relative.
Interface that allows rejection of some matches in intersection queries (e.g.
void clear()
Clear any cached previous clicks and helper lines.
double toDouble(bool *ok) const
void keyPressEvent(QKeyEvent *e) override
key press event on the dock
QString tr(const char *sourceText, const char *disambiguation, int n)
QgsPoint penultimatePoint(bool *exists=nullptr) const
The penultimate point.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:109
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:353
void update(const QRectF &rect)
LockMode lockMode() const
The current lock mode of this constraint.
#define M_PI_2
Definition: util.cpp:45
double y() const
Get the y value of the point.
Definition: qgspoint.h:193
T value(int i) const
Filter key events to e.g.
void pointChanged(const QgsPoint &point)
Sometimes a constraint may change the current point out of a mouse event.
void setValue(const QString &key, const QVariant &value)
QString number(int n, int base)
int count(const T &value) const
snap according to the configuration set in the snapping settings
QString fromUtf8(const char *str, int size)
void installEventFilter(QObject *filterObj)
void setRepeatingLock(bool repeating)
Sets whether a repeating lock is set for the constraint.
int x() const
QList< QgsPoint > snapSegment(SnappingMode snappingMode, bool *snapped=nullptr, bool allLayers=false) const
Returns the first snapped segment.
Qt::MouseButton button() const
void hideEvent(QHideEvent *) override
Disables the CAD tools when hiding the dock.
bool isEmpty() const
QgsDockWidget subclass with more fine-grained control over how the widget is closed or opened...
Definition: qgsdockwidget.h:28
bool isEmpty() const
double mapUnitsPerPixel() const
Return the distance in geographical coordinates that equals to one pixel in the map.
const_iterator constEnd() const
#define M_PI
QPoint pos() const
Create an advanced digitizing dock widget.
iterator end()
void enable()
Enables the tool (call this when an appropriate map tool is set and in the condition to make use of c...
static bool lineCircleIntersection(const QgsPoint &center, const double radius, const QList< QgsPoint > &segment, QgsPoint &intersection)
performs the intersection of a circle and a line
void set(double x, double y)
Sets the x and y value of the point.
Definition: qgspoint.h:176
A class to represent a point.
Definition: qgspoint.h:117
iterator begin()
double ANALYSIS_EXPORT angle(Point3D *p1, Point3D *p2, Point3D *p3, Point3D *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
int key() const
void popWarning()
Remove any previously emitted warnings (if any)
void setX(double x)
Sets the x value of the point.
Definition: qgspoint.h:162
void setMapPoint(const QgsPoint &point)
Set the (snapped) point this event points to in map coordinates.
bool canvasMoveEvent(QgsMapMouseEvent *e)
Will react on a canvas move event.
void setY(double y)
Sets the y value of the point.
Definition: qgspoint.h:170
void setCheckable(bool)
const QgsMapSettings & mapSettings() const
const Key key(const T &value) const
QLineEdit * lineEdit() const
The line edit that manages the value of the constraint.
void setMaximumHeight(int maxh)
void insert(int i, const T &value)
QPoint mapFromGlobal(const QPoint &pos) const
void pushWarning(const QString &message)
Push a warning.
bool canvasPressEvent(QgsMapMouseEvent *e)
Will react on a canvas press event.
virtual bool event(QEvent *event)
double toDouble(bool *ok) const
double sqrDist(double x, double y) const
Returns the squared distance between this point a specified x, y coordinate.
Definition: qgspoint.cpp:353
iterator insert(const Key &key, const T &value)
QgsPoint currentPoint(bool *exists=nullptr) const
The last point.
void show()
static const double SoftConstraintTolerancePixel
void setValue(double value, bool updateWidget=true)
Set the value of the constraint.
QgsPoint snapPoint(SnappingMode snappingMode)
snapPoint will snap the points using the map canvas snapping utils configuration
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
QgsPoint previousPoint(bool *exists=nullptr) const
The previous point.
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
const QgsCoordinateReferenceSystem & destinationCrs() const
returns CRS of destination coordinate reference system
void setEnabled(bool)