40 Q_ASSERT( segment.
count() == 2 );
44 const double x1 = segment[0].x() - center.
x();
45 const double y1 = segment[0].y() - center.
y();
46 const double x2 = segment[1].x() - center.
x();
47 const double y2 = segment[1].y() - center.
y();
48 const double dx = x2 - x1;
49 const double dy = y2 - y1;
51 const double dr = sqrt( pow( dx, 2 ) + pow( dy, 2 ) );
52 const double d = x1 * y2 - x2 * y1;
54 const double disc = pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 );
64 const int sgnDy = dy < 0 ? -1 : 1;
66 const double ax = center.
x() + ( d * dy + sgnDy * dx * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) );
67 const double ay = center.
y() + ( -d * dx + qAbs( dy ) * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) );
70 const double bx = center.
x() + ( d * dy - sgnDy * dx * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) );
71 const double by = center.
y() + ( -d * dx - qAbs( dy ) * sqrt( pow( radius, 2 ) * pow( dr, 2 ) - pow( d, 2 ) ) ) / ( pow( dr, 2 ) );
78 intersection.
set( p1.
x(), p1.
y() );
82 intersection.
set( p2.
x(), p2.
y() );
91 , mMapCanvas( canvas )
92 , mCurrentMapToolSupportsCad( false )
93 , mCadEnabled( false )
94 , mConstructionMode( false )
96 , mCommonAngleConstraint(
QSettings().value(
"/Cad/CommonAngle", 90 ).toInt() )
97 , mSnappedToVertex( false )
98 , mSessionActive( false )
99 , mErrorMessage( nullptr )
105 mAngleConstraint =
new CadConstraint( mAngleLineEdit, mLockAngleButton, mRelativeAngleButton );
106 mDistanceConstraint =
new CadConstraint( mDistanceLineEdit, mLockDistanceButton ) ;
107 mXConstraint =
new CadConstraint( mXLineEdit, mLockXButton, mRelativeXButton );
108 mYConstraint =
new CadConstraint( mYLineEdit, mLockYButton, mRelativeYButton ) ;
112 mAngleLineEdit->installEventFilter(
this );
113 mDistanceLineEdit->installEventFilter(
this );
114 mXLineEdit->installEventFilter(
this );
115 mYLineEdit->installEventFilter(
this );
118 mEnableAction =
new QAction(
this );
119 mEnableAction->
setText(
tr(
"Enable advanced digitizing tools" ) );
122 mEnabledButton->addAction( mEnableAction );
123 mEnabledButton->setDefaultAction( mEnableAction );
126 connect( mEnableAction, SIGNAL( triggered(
bool ) ),
this, SLOT( activateCad(
bool ) ) );
127 connect( mConstructionModeButton, SIGNAL( clicked(
bool ) ),
this, SLOT( setConstructionMode(
bool ) ) );
128 connect( mParallelButton, SIGNAL( clicked(
bool ) ),
this, SLOT( addtionalConstraintClicked(
bool ) ) );
129 connect( mPerpendicularButton, SIGNAL( clicked(
bool ) ),
this, SLOT( addtionalConstraintClicked(
bool ) ) );
130 connect( mLockAngleButton, SIGNAL( clicked(
bool ) ),
this, SLOT( lockConstraint(
bool ) ) );
131 connect( mLockDistanceButton, SIGNAL( clicked(
bool ) ),
this, SLOT( lockConstraint(
bool ) ) );
132 connect( mLockXButton, SIGNAL( clicked(
bool ) ),
this, SLOT( lockConstraint(
bool ) ) );
133 connect( mLockYButton, SIGNAL( clicked(
bool ) ),
this, SLOT( lockConstraint(
bool ) ) );
134 connect( mRelativeAngleButton, SIGNAL( clicked(
bool ) ),
this, SLOT( setConstraintRelative(
bool ) ) );
135 connect( mRelativeXButton, SIGNAL( clicked(
bool ) ),
this, SLOT( setConstraintRelative(
bool ) ) );
136 connect( mRelativeYButton, SIGNAL( clicked(
bool ) ),
this, SLOT( setConstraintRelative(
bool ) ) );
137 connect( mAngleLineEdit, SIGNAL( returnPressed() ),
this, SLOT( lockConstraint() ) );
138 connect( mDistanceLineEdit, SIGNAL( returnPressed() ),
this, SLOT( lockConstraint() ) );
139 connect( mXLineEdit, SIGNAL( returnPressed() ),
this, SLOT( lockConstraint() ) );
140 connect( mYLineEdit, SIGNAL( returnPressed() ),
this, SLOT( lockConstraint() ) );
148 commonAngles << QPair<int, QString>( 0,
tr(
"Do not snap to common angles" ) );
156 action->
setChecked( it->first == mCommonAngleConstraint );
159 mCommonAngleActions.
insert( action, it->first );
173 action->
setChecked( it->first == mSnappingMode );
176 mSnappingActions.
insert( action, it->first );
179 mSettingsButton->setMenu( menu );
180 connect( mSettingsButton, SIGNAL( triggered(
QAction* ) ),
this, SLOT( settingsButtonTriggered(
QAction* ) ) );
182 updateCapacity(
true );
189 setCadEnabled(
false );
192 void QgsAdvancedDigitizingDockWidget::setCadEnabled(
bool enabled )
196 mCadButtons->setEnabled( enabled );
197 mInputWidgets->setEnabled( enabled );
201 setConstructionMode(
false );
204 void QgsAdvancedDigitizingDockWidget::activateCad(
bool enabled )
206 enabled &= mCurrentMapToolSupportsCad;
215 setCadEnabled( enabled );
218 void QgsAdvancedDigitizingDockWidget::addtionalConstraintClicked(
bool activated )
224 if (
sender() == mParallelButton )
226 lockAdditionalConstraint(
Parallel );
228 else if (
sender() == mPerpendicularButton )
235 void QgsAdvancedDigitizingDockWidget::setConstraintRelative(
bool activate )
237 if (
sender() == mRelativeAngleButton )
241 else if (
sender() == mRelativeXButton )
245 else if (
sender() == mRelativeYButton )
251 void QgsAdvancedDigitizingDockWidget::setConstructionMode(
bool enabled )
254 mConstructionModeButton->setChecked( enabled );
257 void QgsAdvancedDigitizingDockWidget::settingsButtonTriggered(
QAction* action )
261 if ( isn != mSnappingActions.
constEnd() )
263 isn.
key()->setChecked(
true );
264 mSnappingMode = isn.
value();
271 if ( ica != mCommonAngleActions.
constEnd() )
273 ica.
key()->setChecked(
true );
274 mCommonAngleConstraint = ica.
value();
280 void QgsAdvancedDigitizingDockWidget::releaseLocks()
293 void QgsAdvancedDigitizingDockWidget::emit
pointChanged()
298 QMouseEvent* e =
new QMouseEvent( QEvent::MouseMove, pos, globalPos, Qt::NoButton, Qt::NoButton, Qt::NoModifier );
299 mCurrentMapTool->canvasMoveEvent( e );
303 void QgsAdvancedDigitizingDockWidget::lockConstraint(
bool activate )
307 if ( obj == mAngleLineEdit || obj == mLockAngleButton )
309 constraint = mAngleConstraint;
311 else if ( obj == mDistanceLineEdit || obj == mLockDistanceButton )
313 constraint = mDistanceConstraint;
315 else if ( obj == mXLineEdit || obj == mLockXButton )
317 constraint = mXConstraint;
319 else if ( obj == mYLineEdit || obj == mLockYButton )
321 constraint = mYConstraint;
332 double value = textValue.
toDouble( &ok );
365 if ( constraint == mAngleConstraint )
375 void QgsAdvancedDigitizingDockWidget::lockAdditionalConstraint(
AdditionalConstraint constraint )
377 mAdditionalConstraint = constraint;
378 mPerpendicularButton->setChecked( constraint ==
Perpendicular );
379 mParallelButton->setChecked( constraint ==
Parallel );
382 void QgsAdvancedDigitizingDockWidget::updateCapacity(
bool updateUIwithoutChange )
384 CadCapacities newCapacities =
nullptr;
386 if ( mCadPointList.
count() > 1 )
390 if ( mCadPointList.
count() > 2 )
394 if ( !updateUIwithoutChange && newCapacities == mCapacities )
402 bool relativeAngle = mCadEnabled && newCapacities.testFlag(
RelativeAngle );
403 bool absoluteAngle = mCadEnabled && newCapacities.testFlag(
AbsoluteAngle );
406 mPerpendicularButton->setEnabled( absoluteAngle );
407 mParallelButton->setEnabled( absoluteAngle );
408 if ( !absoluteAngle )
414 mLockAngleButton->setEnabled( absoluteAngle );
415 mRelativeAngleButton->setEnabled( relativeAngle );
416 mAngleLineEdit->setEnabled( absoluteAngle );
417 if ( !absoluteAngle )
421 if ( !relativeAngle )
425 else if ( relativeAngle && !mCapacities.testFlag(
RelativeAngle ) )
432 mLockDistanceButton->setEnabled( relativeCoordinates );
433 mDistanceLineEdit->setEnabled( relativeCoordinates );
434 if ( !relativeCoordinates )
439 mRelativeXButton->setEnabled( relativeCoordinates );
440 mRelativeYButton->setEnabled( relativeCoordinates );
443 mCapacities = newCapacities;
451 QgsDebugMsg(
"Constraints (locked / relative / value" );
461 bool previousPointExist, penulPointExist;
475 point.
setX( previousPt.
x() + mXConstraint->
value() );
480 const double dx = mSnappedSegment.
at( 1 ).x() - mSnappedSegment.
at( 0 ).x();
483 point.
setY( mSnappedSegment.
at( 0 ).y() );
487 const double dy = mSnappedSegment.
at( 1 ).y() - mSnappedSegment.
at( 0 ).y();
488 point.
setY( mSnappedSegment.
at( 0 ).y() + ( dy * ( point.
x() - mSnappedSegment.
at( 0 ).x() ) ) / dx );
502 point.
setY( previousPt.
y() + mYConstraint->
value() );
507 const double dy = mSnappedSegment.
at( 1 ).y() - mSnappedSegment.
at( 0 ).y();
510 point.
setX( mSnappedSegment.
at( 0 ).x() );
514 const double dx = mSnappedSegment.
at( 1 ).x() - mSnappedSegment.
at( 0 ).x();
515 point.
setX( mSnappedSegment.
at( 0 ).x() + ( dx * ( point.
y() - mSnappedSegment.
at( 0 ).y() ) ) / dy );
527 if ( !mAngleConstraint->
isLocked() && mCapacities.testFlag(
AbsoluteAngle ) && mCommonAngleConstraint != 0 )
529 double commonAngle = mCommonAngleConstraint *
M_PI / 180;
532 double softAngle = qAtan2( point.
y() - previousPt.
y(),
533 point.
x() - previousPt.
x() );
534 double deltaAngle = 0;
538 deltaAngle = qAtan2( previousPt.
y() - penultimatePt.
y(),
539 previousPt.
x() - penultimatePt.
x() );
540 softAngle -= deltaAngle;
542 int quo = qRound( softAngle / commonAngle );
546 softAngle = quo * commonAngle ;
549 const double dist = qAbs( qCos( softAngle + deltaAngle ) * ( previousPt.
y() - point.
y() )
550 - qSin( softAngle + deltaAngle ) * ( previousPt.
x() - point.
x() ) );
560 double angleValue = mAngleConstraint->
value() *
M_PI / 180;
564 angleValue += qAtan2( previousPt.
y() - penultimatePt.
y(),
565 previousPt.
x() - penultimatePt.
x() );
568 double cosa = qCos( angleValue );
569 double sina = qSin( angleValue );
570 double v = ( point.
x() - previousPt.
x() ) * cosa + ( point.
y() - previousPt.
y() ) * sina ;
575 else if ( mXConstraint->
isLocked() )
583 double x = mXConstraint->
value();
588 point.
setY( previousPt.
y() + x * sina / cosa );
591 else if ( mYConstraint->
isLocked() )
599 double y = mYConstraint->
value();
604 point.
setX( previousPt.
x() + y * cosa / sina );
609 point.
setX( previousPt.
x() + cosa * v );
610 point.
setY( previousPt.
y() + sina * v );
618 const double x1 = previousPt.
x();
619 const double y1 = previousPt.
y();
620 const double x2 = previousPt.
x() + cosa;
621 const double y2 = previousPt.
y() + sina;
623 const double x3 = mSnappedSegment.
at( 0 ).x();
624 const double y3 = mSnappedSegment.
at( 0 ).y();
625 const double x4 = mSnappedSegment.
at( 1 ).x();
626 const double y4 = mSnappedSegment.
at( 1 ).y();
628 const double d = ( x1 - x2 ) * ( y3 - y4 ) - ( y1 - y2 ) * ( x3 - x4 );
632 if ( qAbs( d ) > 0.01 )
634 point.
setX((( x3 - x4 )*( x1*y2 - y1*x2 ) - ( x1 - x2 )*( x3*y4 - y3*x4 ) ) / d );
635 point.
setY((( y3 - y4 )*( x1*y2 - y1*x2 ) - ( y1 - y2 )*( x3*y4 - y3*x4 ) ) / d );
641 if ( mDistanceConstraint->
isLocked() && previousPointExist )
663 const double dist = sqrt( point.
sqrDist( previousPt ) );
668 point.
set( previousPt.
x() + mDistanceConstraint->
value(), previousPt.
y() );
672 const double vP = mDistanceConstraint->
value() / dist;
673 point.
set( previousPt.
x() + ( point.
x() - previousPt.
x() ) * vP,
674 previousPt.
y() + ( point.
y() - previousPt.
y() ) * vP );
689 QgsDebugMsg(
QString(
"penultimate point: %1 %2" ).arg( penultimatePt.
x() ).arg( penultimatePt.
y() ) );
697 updateCurrentPoint( point );
702 if ( !mAngleConstraint->
isLocked() && previousPointExist )
705 if ( penulPointExist && mAngleConstraint->
relative() )
708 angle = qAtan2( previousPt.
y() - penultimatePt.
y(),
709 previousPt.
x() - penultimatePt.
x() );
711 angle = ( qAtan2( point.
y() - previousPt.
y(),
712 point.
x() - previousPt.
x()
713 ) - angle ) * 180 /
M_PI;
715 angle = fmod( angle, 360.0 );
716 mAngleConstraint->
setValue( angle );
719 if ( !mDistanceConstraint->
isLocked() && previousPointExist )
726 if ( previousPointExist && mXConstraint->
relative() )
728 mXConstraint->
setValue( point.
x() - previousPt.
x() );
738 if ( previousPointExist && mYConstraint->
relative() )
740 mYConstraint->
setValue( point.
y() - previousPt.
y() );
759 bool previousPointExist, penulPointExist, mSnappedSegmentExist;
764 if ( !previousPointExist || !mSnappedSegmentExist )
769 double angle = qAtan2( mSnappedSegment[0].
y() - mSnappedSegment[1].
y(), mSnappedSegment[0].
x() - mSnappedSegment[1].
x() );
771 if ( mAngleConstraint->
relative() && penulPointExist )
773 angle -= qAtan2( previousPt.
y() - penultimatePt.
y(), previousPt.
x() - penultimatePt.
x() );
783 mAngleConstraint->
setValue( angle );
796 return mCadEnabled && mConstructionMode;
806 if ( e->
button() == Qt::RightButton )
815 if ( alignToSegment( e ) )
829 if ( e->
button() == Qt::LeftButton )
832 if ( !mConstructionMode && !captureSegment )
837 return mConstructionMode;
847 emit
pushWarning(
tr(
"Some constraints are incompatible. Resulting point might be incorrect." ) );
871 case Qt::Key_Backspace:
874 removePreviousPoint();
908 case Qt::Key_Backspace:
911 removePreviousPoint();
932 if ( event->
type() != QEvent::KeyPress )
941 return filterKeyPress( keyEvent ) ;
944 bool QgsAdvancedDigitizingDockWidget::filterKeyPress(
QKeyEvent* e )
955 else if ( e->
modifiers() == Qt::ShiftModifier )
965 mXLineEdit->setFocus();
966 mXLineEdit->selectAll();
977 else if ( e->
modifiers() == Qt::ShiftModifier )
987 mYLineEdit->setFocus();
988 mYLineEdit->selectAll();
1002 else if ( e->
modifiers() == Qt::ShiftModifier )
1012 mAngleLineEdit->setFocus();
1013 mAngleLineEdit->selectAll();
1029 mDistanceLineEdit->setFocus();
1030 mDistanceLineEdit->selectAll();
1036 setConstructionMode( !mConstructionMode );
1041 bool parallel = mParallelButton->isChecked();
1042 bool perpendicular = mPerpendicularButton->isChecked();
1044 if ( !parallel && !perpendicular )
1048 else if ( perpendicular )
1050 lockAdditionalConstraint(
Parallel );
1070 mErrorLabel->setText(
tr(
"CAD tools can not be used on geographic coordinates. Change the coordinates system in the project properties." ) );
1071 mErrorLabel->show();
1073 setCadEnabled(
false );
1078 mErrorLabel->hide();
1082 mCurrentMapToolSupportsCad =
true;
1088 setCadEnabled( mSessionActive );
1095 mErrorLabel->setText(
tr(
"CAD tools are not enabled for the current map tool" ) );
1096 mErrorLabel->show();
1100 mCurrentMapToolSupportsCad =
false;
1102 setCadEnabled(
false );
1105 void QgsAdvancedDigitizingDockWidget::addPoint(
const QgsPoint& point )
1109 mCadPointList << point;
1113 mCadPointList.
insert( 0, point );
1119 void QgsAdvancedDigitizingDockWidget::removePreviousPoint()
1129 void QgsAdvancedDigitizingDockWidget::clearPoints()
1131 mCadPointList.
clear();
1132 mSnappedSegment.
clear();
1133 mSnappedToVertex =
false;
1138 void QgsAdvancedDigitizingDockWidget::updateCurrentPoint(
const QgsPoint& point )
1142 mCadPointList << point;
1147 mCadPointList[0] = point ;
1155 mLockerButton->setChecked( mode == HardLock );
1157 if ( mode == NoLock )
1165 mRelative = relative;
1166 if ( mRelativeButton )
1168 mRelativeButton->setChecked( relative );
1180 setLockMode( mLockMode == HardLock ? NoLock : HardLock );
1185 setRelative( mRelative ?
false :
true );
1193 return mCadPointList.
value( 0 );
1203 return mCadPointList.
value( 1 );
1213 return mCadPointList.
value( 2 );
void setText(const QString &text)
Class for parsing and evaluation of expressions (formerly called "search strings").
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
Qt::KeyboardModifiers modifiers() const
QgsPoint mapPoint() const
mapPoint returns the point in coordinates
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())
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
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
QAction * addAction(QAction *action)
const_iterator constFind(const Key &key) const
Interface that allows rejection of some matches in intersection queries (e.g.
double toDouble(bool *ok) const
double sqrDist(double x, double y) const
Returns the squared distance between this point and x,y.
QString tr(const char *sourceText, const char *disambiguation, int n)
Map canvas is a class for displaying all GIS data types on a canvas.
void update(const QRectF &rect)
double x() const
Get the x value of the point.
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)
const QgsCoordinateReferenceSystem & destinationCrs() const
returns CRS of destination coordinate reference system
const_iterator constEnd() const
double mapUnitsPerPixel() const
Return the distance in geographical coordinates that equals to one pixel in the map.
void set(double x, double y)
Sets the x and y value of the point.
A class to represent a point.
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)
void setX(double x)
Sets the x value of the point.
void setMapPoint(const QgsPoint &point)
Set the (snapped) point this event points to in map coordinates.
void setY(double y)
Sets the y value of the point.
const Key key(const T &value) const
void insert(int i, const T &value)
double y() const
Get the y value of the point.
QList< QgsPoint > snapSegment(SnappingMode snappingMode, bool *snapped=nullptr, bool allLayers=false) const
Returns the first snapped segment.
double toDouble(bool *ok) const
iterator insert(const Key &key, const T &value)
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)
bool geographicFlag() const
Returns whether the CRS is a geographic CRS.
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
The QgsAdvancedDigitizingCanvasItem class draws the graphical elements of the CAD tools (...
const T value(const Key &key) const