QGIS API Documentation 3.41.0-Master (45a0abf3bec)
Loading...
Searching...
No Matches
qgsadvanceddigitizingtools.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsadvanceddigitizingintersectiontools.cpp
3 ----------------------
4 begin : May 2024
5 copyright : (C) Mathieu Pellerin
6 email : mathieu at opengis dot ch
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 <QMouseEvent>
17#include <QEnterEvent>
18#include <QLocale>
19
21#include "moc_qgsadvanceddigitizingtools.cpp"
22#include "qgsapplication.h"
23#include "qgsdoublespinbox.h"
24#include "qgsmapcanvas.h"
25
27 : QObject( canvas ? canvas->viewport() : nullptr )
28 , mMapCanvas( canvas )
29 , mCadDockWidget( cadDockWidget )
30{
31}
32
37
39{
40 if ( mToolWidget )
41 {
42 mToolWidget->deleteLater();
43 }
44}
45
47{
48 QWidget *toolWidget = new QWidget();
49
50 QGridLayout *layout = new QGridLayout( toolWidget );
51 layout->setContentsMargins( 0, 0, 0, 0 );
52 toolWidget->setLayout( layout );
53
54 QLabel *label = new QLabel( tr( "Circle #1" ), toolWidget );
55 layout->addWidget( label, 0, 0, 1, 3 );
56
57 mCircle1Digitize = new QToolButton( toolWidget );
58 mCircle1Digitize->setCheckable( true );
59 mCircle1Digitize->setChecked( false );
60 mCircle1Digitize->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionMapIdentification.svg" ) ) );
61 connect( mCircle1Digitize, &QAbstractButton::toggled, this, [ = ]( bool checked )
62 {
63 if ( checked )
64 {
65 mCircle2Digitize->setChecked( false );
66 }
67 } );
68 layout->addWidget( mCircle1Digitize, 1, 2, 2, 1 );
69
70 label = new QLabel( QStringLiteral( "x" ), toolWidget );
71 layout->addWidget( label, 1, 0 );
72
73 mCircle1X = new QgsDoubleSpinBox( toolWidget );
74 mCircle1X->setToolTip( tr( "X coordinate" ) );
75 mCircle1X->setMinimum( std::numeric_limits<double>::lowest() );
76 mCircle1X->setMaximum( std::numeric_limits<double>::max() );
77 mCircle1X->setDecimals( mCadDockWidget->constraintX()->precision() );
78 mCircle1X->setClearValue( 0.0 );
79 connect( mCircle1X, &QgsDoubleSpinBox::textEdited, this, [ = ]() { mCircle1Digitize->setChecked( false ); } );
80 layout->addWidget( mCircle1X, 1, 1 );
81
82 label = new QLabel( QStringLiteral( "y" ), toolWidget );
83 layout->addWidget( label, 2, 0 );
84
85 mCircle1Y = new QgsDoubleSpinBox( toolWidget );
86 mCircle1Y->setToolTip( tr( "Y coordinate" ) );
87 mCircle1Y->setMinimum( std::numeric_limits<double>::lowest() );
88 mCircle1Y->setMaximum( std::numeric_limits<double>::max() );
89 mCircle1Y->setDecimals( mCadDockWidget->constraintY()->precision() );
90 mCircle1Y->setClearValue( 0.0 );
91 connect( mCircle1Y, &QgsDoubleSpinBox::textEdited, this, [ = ]() { mCircle1Digitize->setChecked( false ); } );
92 layout->addWidget( mCircle1Y, 2, 1 );
93
94 label = new QLabel( QStringLiteral( "d" ), toolWidget );
95 layout->addWidget( label, 3, 0 );
96
97 mCircle1Distance = new QgsDoubleSpinBox( toolWidget );
98 mCircle1Distance->setToolTip( tr( "Distance" ) );
99 mCircle1Distance->setMinimum( 0 );
100 mCircle1Distance->setMaximum( std::numeric_limits<double>::max() );
101 mCircle1Distance->setDecimals( mCadDockWidget->constraintX()->precision() );
102 connect( mCircle1Distance, &QgsDoubleSpinBox::returnPressed, this, [ = ]() { mCircle2Digitize->setChecked( true ); } );
103 layout->addWidget( mCircle1Distance, 3, 1 );
104
105 label = new QLabel( tr( "Circle #2" ), toolWidget );
106 layout->addWidget( label, 4, 0, 1, 3 );
107
108 mCircle2Digitize = new QToolButton( toolWidget );
109 mCircle2Digitize->setCheckable( true );
110 mCircle2Digitize->setChecked( false );
111 mCircle2Digitize->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionMapIdentification.svg" ) ) );
112 connect( mCircle2Digitize, &QAbstractButton::toggled, this, [ = ]( bool checked )
113 {
114 if ( checked )
115 {
116 mCircle1Digitize->setChecked( false );
117 }
118 } );
119 layout->addWidget( mCircle2Digitize, 5, 2, 2, 1 );
120
121 label = new QLabel( QStringLiteral( "x" ), toolWidget );
122 layout->addWidget( label, 5, 0 );
123
124 mCircle2X = new QgsDoubleSpinBox( toolWidget );
125 mCircle2X->setToolTip( tr( "X coordinate" ) );
126 mCircle2X->setMinimum( std::numeric_limits<double>::lowest() );
127 mCircle2X->setMaximum( std::numeric_limits<double>::max() );
128 mCircle2X->setDecimals( mCadDockWidget->constraintX()->precision() );
129 mCircle2X->setClearValue( 0.0 );
130 connect( mCircle2X, &QgsDoubleSpinBox::textEdited, this, [ = ]() { mCircle2Digitize->setChecked( false ); } );
131 layout->addWidget( mCircle2X, 5, 1 );
132
133 label = new QLabel( QStringLiteral( "y" ), toolWidget );
134 layout->addWidget( label, 6, 0 );
135
136 mCircle2Y = new QgsDoubleSpinBox( toolWidget );
137 mCircle2Y->setToolTip( tr( "Y coordinate" ) );
138 mCircle2Y->setMinimum( std::numeric_limits<double>::lowest() );
139 mCircle2Y->setMaximum( std::numeric_limits<double>::max() );
140 mCircle2Y->setDecimals( mCadDockWidget->constraintY()->precision() );
141 mCircle2Y->setClearValue( 0.0 );
142 connect( mCircle2Y, &QgsDoubleSpinBox::textEdited, this, [ = ]() { mCircle2Digitize->setChecked( false ); } );
143 layout->addWidget( mCircle2Y, 6, 1 );
144
145 label = new QLabel( QStringLiteral( "d" ), toolWidget );
146 layout->addWidget( label, 7, 0 );
147
148 mCircle2Distance = new QgsDoubleSpinBox( toolWidget );
149 mCircle1Distance->setToolTip( tr( "Distance" ) );
150 mCircle2Distance->setMinimum( 0 );
151 mCircle2Distance->setMaximum( std::numeric_limits<double>::max() );
152 mCircle2Distance->setDecimals( mCadDockWidget->constraintX()->precision() );
153 layout->addWidget( mCircle2Distance, 7, 1 );
154
155 connect( mCircle1X, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } );
156 connect( mCircle1Y, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } );
157 connect( mCircle1Distance, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } );
158 connect( mCircle2X, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } );
159 connect( mCircle2Y, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } );
160 connect( mCircle2Distance, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } );
161
162
163 bool focusOnCircle2 = false;
164 if ( mCadDockWidget )
165 {
166 if ( mCadDockWidget->constraintDistance()->isLocked() )
167 {
168 QgsPoint point = mCadDockWidget->previousPointV2();
169 if ( !point.isEmpty() )
170 {
171 whileBlocking( mCircle1Distance )->setValue( mCadDockWidget->constraintDistance()->value() );
172 whileBlocking( mCircle1X )->setValue( point.x() );
173 whileBlocking( mCircle1Y )->setValue( point.y() );
174 mP1 = point;
175 focusOnCircle2 = true;
176
177 mCadDockWidget->toggleConstraintDistance();
178 }
179 }
180 }
181
182 if ( focusOnCircle2 )
183 {
184 mCircle2Digitize->setChecked( true );
185 }
186 else
187 {
188 mCircle1Digitize->setChecked( true );
189 }
190
191 toolWidget->installEventFilter( this );
192
193 mToolWidget = toolWidget;
194 return toolWidget;
195}
196
197void QgsAdvancedDigitizingCirclesIntersectionTool::processParameters()
198{
199 mP1 = QgsPointXY();
200 mP2 = QgsPointXY();
201 QgsGeometryUtils::circleCircleIntersections( QgsPointXY( mCircle1X->value(), mCircle1Y->value() ), mCircle1Distance->value(),
202 QgsPointXY( mCircle2X->value(), mCircle2Y->value() ), mCircle2Distance->value(),
203 mP1, mP2 );
204 emit paintRequested();
205}
206
208{
209 if ( mCircle1Digitize->isChecked() )
210 {
211 mCircle1X->setValue( event->mapPoint().x() );
212 mCircle1Y->setValue( event->mapPoint().y() );
213 }
214 else if ( mCircle2Digitize->isChecked() )
215 {
216 mCircle2X->setValue( event->mapPoint().x() );
217 mCircle2Y->setValue( event->mapPoint().y() );
218 }
219
220 if ( !mP1.isEmpty() )
221 {
222 mP1Closest = QgsGeometryUtils::distance2D( QgsPoint( mP1 ), QgsPoint( event->mapPoint() ) ) < QgsGeometryUtils::distance2D( QgsPoint( mP2 ), QgsPoint( event->mapPoint() ) );
223 event->setMapPoint( mP1Closest ? mP1 : mP2 );
224 }
225 else
226 {
227 event->setAccepted( false );
228 }
229}
230
232{
233 if ( event->button() == Qt::RightButton )
234 {
235 deleteLater();
236 mCadDockWidget->updateCadPaintItem();
237 event->setAccepted( false );
238 return;
239 }
240
241 if ( mCircle1Digitize->isChecked() )
242 {
243 mCircle1X->setValue( event->mapPoint().x() );
244 mCircle1Y->setValue( event->mapPoint().y() );
245 mCircle1Digitize->setChecked( false );
246 mCircle1Distance->setFocus();
247 mCircle1Distance->selectAll();
248 event->setAccepted( false );
249 return;
250 }
251 else if ( mCircle2Digitize->isChecked() )
252 {
253 mCircle2X->setValue( event->mapPoint().x() );
254 mCircle2Y->setValue( event->mapPoint().y() );
255 mCircle2Digitize->setChecked( false );
256 mCircle2Distance->setFocus();
257 mCircle2Distance->selectAll();
258 event->setAccepted( false );
259 return;
260 }
261
262 if ( !mP1.isEmpty() )
263 {
264 mP1Closest = QgsGeometryUtils::distance2D( QgsPoint( mP1 ), QgsPoint( event->mapPoint() ) ) < QgsGeometryUtils::distance2D( QgsPoint( mP2 ), QgsPoint( event->mapPoint() ) );
265 event->setMapPoint( mP1Closest ? mP1 : mP2 );
266 deleteLater();
267 return;
268 }
269
270 event->setAccepted( false );
271}
272
273void QgsAdvancedDigitizingCirclesIntersectionTool::drawCircle( QPainter *painter, double x, double y, double distance )
274{
275 painter->setPen( QPen( QColor( 0, 127, 0, 200 ), 2 ) );
276 painter->setBrush( Qt::NoBrush );
277
279 painter->drawLine( QLineF( x - 3, y - 3, x + 3, y + 3 ) );
280 painter->drawLine( QLineF( x - 3, y + 3, x + 3, y - 3 ) );
281
282 painter->setPen( QPen( QColor( 0, 127, 0, 150 ), 1, Qt::DashLine ) );
283 distance = distance / mapCanvas()->mapSettings().mapUnitsPerPixel();
284 painter->drawEllipse( QRectF( x - distance, y - distance, distance * 2, distance * 2 ) );
285}
286
287void QgsAdvancedDigitizingCirclesIntersectionTool::drawCandidate( QPainter *painter, double x, double y, bool closest )
288{
289 if ( closest )
290 {
291 painter->setPen( QPen( QColor( 127, 0, 0, 222 ), 4 ) );
292 }
293 else
294 {
295 painter->setPen( QPen( QColor( 0, 127, 0, 222 ), 2 ) );
296 }
297
299 double size = closest ? 5 : 3;
300 painter->drawRect( QRectF( x - size, y - size, size * 2, size * 2 ) );
301}
302
304{
305 painter->save();
306
307 drawCircle( painter, mCircle1X->value(), mCircle1Y->value(), mCircle1Distance->value() );
308 drawCircle( painter, mCircle2X->value(), mCircle2Y->value(), mCircle2Distance->value() );
309
310 if ( !mP1.isEmpty() )
311 {
312 if ( mP1Closest )
313 {
314 drawCandidate( painter, mP2.x(), mP2.y(), false );
315 drawCandidate( painter, mP1.x(), mP1.y(), true );
316 }
317 else
318 {
319 drawCandidate( painter, mP1.x(), mP1.y(), false );
320 drawCandidate( painter, mP2.x(), mP2.y(), true );
321 }
322 }
323
324 painter->restore();
325}
326
327bool QgsAdvancedDigitizingCirclesIntersectionTool::eventFilter( QObject *obj, QEvent *event )
328{
329 if ( event->type() == QEvent::ShortcutOverride || event->type() == QEvent::KeyPress )
330 {
331 if ( QKeyEvent *keyEvent = dynamic_cast<QKeyEvent *>( event ) )
332 {
333 if ( keyEvent->key() == Qt::Key_Escape )
334 {
335 deleteLater();
336 mCadDockWidget->updateCadPaintItem();
337 }
338 }
339 }
340
341 return QObject::eventFilter( obj, event );
342}
void canvasReleaseEvent(QgsMapMouseEvent *event) override
Handles canvas release event.
QWidget * createWidget() override
Returns a widget to control the tool.
QgsAdvancedDigitizingCirclesIntersectionTool(QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget)
The advanced digitizing's circles intersection tool constructor.
void canvasMoveEvent(QgsMapMouseEvent *event) override
Handles canvas press move.
void paint(QPainter *painter) override
Paints tool content onto the advanced digitizing canvas item.
The QgsAdvancedDigitizingDockWidget class is a dockable widget used to handle the CAD tools on top of...
An abstract class for advanced digitizing tools.
QgsMapCanvas * mapCanvas() const
Returns the map canvas associated with the tool.
QPointer< QgsAdvancedDigitizingDockWidget > mCadDockWidget
void paintRequested()
Requests a new painting event to the advanced digitizing canvas item.
QgsAdvancedDigitizingTool(QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget)
The advanced digitizing tool constructor.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
The QgsSpinBox is a spin box with a clear button that will set the value to the defined clear value.
void textEdited(const QString &text)
Emitted when the the value has been manually edited via line edit.
void setClearValue(double customValue, const QString &clearValueText=QString())
Defines the clear value as a custom value and will automatically set the clear value mode to CustomVa...
void returnPressed()
Emitted when the Return or Enter key is used in the line edit.
static int circleCircleIntersections(const QgsPointXY &center1, double radius1, const QgsPointXY &center2, double radius2, QgsPointXY &intersection1, QgsPointXY &intersection2)
Calculates the intersections points between the circle with center center1 and radius radius1 and the...
static Q_DECL_DEPRECATED double distance2D(double x1, double y1, double x2, double y2)
Returns the 2D distance between (x1, y1) and (x2, y2).
Map canvas is a class for displaying all GIS data types on a canvas.
const QgsMapToPixel * getCoordinateTransform()
Gets the current coordinate transform.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
A QgsMapMouseEvent is the result of a user interaction with the mouse on a QgsMapCanvas.
QgsPointXY mapPoint() const
mapPoint returns the point in coordinates
double mapUnitsPerPixel() const
Returns the distance in geographical coordinates that equals to one pixel in the map.
void transformInPlace(double &x, double &y) const
Transforms map coordinates to device coordinates.
A class to represent a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
bool isEmpty() const
Returns true if the geometry is empty.
Definition qgspointxy.h:242
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
double x
Definition qgspoint.h:52
bool isEmpty() const override
Returns true if the geometry is empty.
Definition qgspoint.cpp:738
double y
Definition qgspoint.h:53
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:5862