QGIS API Documentation 3.39.0-Master (7b5d8bea57d)
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 "qgsapplication.h"
22#include "qgsdoublespinbox.h"
23#include "qgsmapcanvas.h"
24
26 : QObject( canvas ? canvas->viewport() : nullptr )
27 , mMapCanvas( canvas )
28 , mCadDockWidget( cadDockWidget )
29{
30}
31
36
38{
39 if ( mToolWidget )
40 {
41 mToolWidget->deleteLater();
42 }
43}
44
46{
47 QWidget *toolWidget = new QWidget();
48
49 QGridLayout *layout = new QGridLayout( toolWidget );
50 layout->setContentsMargins( 0, 0, 0, 0 );
51 toolWidget->setLayout( layout );
52
53 QLabel *label = new QLabel( tr( "Circle #1" ), toolWidget );
54 layout->addWidget( label, 0, 0, 1, 3 );
55
56 mCircle1Digitize = new QToolButton( toolWidget );
57 mCircle1Digitize->setCheckable( true );
58 mCircle1Digitize->setChecked( false );
59 mCircle1Digitize->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionMapIdentification.svg" ) ) );
60 connect( mCircle1Digitize, &QAbstractButton::toggled, this, [ = ]( bool checked )
61 {
62 if ( checked )
63 {
64 mCircle2Digitize->setChecked( false );
65 }
66 } );
67 layout->addWidget( mCircle1Digitize, 1, 2, 2, 1 );
68
69 label = new QLabel( QStringLiteral( "x" ), toolWidget );
70 layout->addWidget( label, 1, 0 );
71
72 mCircle1X = new QgsDoubleSpinBox( toolWidget );
73 mCircle1X->setToolTip( tr( "X coordinate" ) );
74 mCircle1X->setMinimum( std::numeric_limits<double>::lowest() );
75 mCircle1X->setMaximum( std::numeric_limits<double>::max() );
76 mCircle1X->setClearValue( 0.0 );
77 connect( mCircle1X, &QgsDoubleSpinBox::textEdited, this, [ = ]() { mCircle1Digitize->setChecked( false ); } );
78 layout->addWidget( mCircle1X, 1, 1 );
79
80 label = new QLabel( QStringLiteral( "y" ), toolWidget );
81 layout->addWidget( label, 2, 0 );
82
83 mCircle1Y = new QgsDoubleSpinBox( toolWidget );
84 mCircle1Y->setToolTip( tr( "Y coordinate" ) );
85 mCircle1Y->setMinimum( std::numeric_limits<double>::lowest() );
86 mCircle1Y->setMaximum( std::numeric_limits<double>::max() );
87 mCircle1Y->setClearValue( 0.0 );
88 connect( mCircle1Y, &QgsDoubleSpinBox::textEdited, this, [ = ]() { mCircle1Digitize->setChecked( false ); } );
89 layout->addWidget( mCircle1Y, 2, 1 );
90
91 label = new QLabel( QStringLiteral( "d" ), toolWidget );
92 layout->addWidget( label, 3, 0 );
93
94 mCircle1Distance = new QgsDoubleSpinBox( toolWidget );
95 mCircle1Distance->setToolTip( tr( "Distance" ) );
96 mCircle1Distance->setMinimum( 0 );
97 mCircle1Distance->setMaximum( std::numeric_limits<double>::max() );
98 connect( mCircle1Distance, &QgsDoubleSpinBox::returnPressed, this, [ = ]() { mCircle2Digitize->setChecked( true ); } );
99 layout->addWidget( mCircle1Distance, 3, 1 );
100
101 label = new QLabel( tr( "Circle #2" ), toolWidget );
102 layout->addWidget( label, 4, 0, 1, 3 );
103
104 mCircle2Digitize = new QToolButton( toolWidget );
105 mCircle2Digitize->setCheckable( true );
106 mCircle2Digitize->setChecked( false );
107 mCircle2Digitize->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionMapIdentification.svg" ) ) );
108 connect( mCircle2Digitize, &QAbstractButton::toggled, this, [ = ]( bool checked )
109 {
110 if ( checked )
111 {
112 mCircle1Digitize->setChecked( false );
113 }
114 } );
115 layout->addWidget( mCircle2Digitize, 5, 2, 2, 1 );
116
117 label = new QLabel( QStringLiteral( "x" ), toolWidget );
118 layout->addWidget( label, 5, 0 );
119
120 mCircle2X = new QgsDoubleSpinBox( toolWidget );
121 mCircle2X->setToolTip( tr( "X coordinate" ) );
122 mCircle2X->setMinimum( std::numeric_limits<double>::lowest() );
123 mCircle2X->setMaximum( std::numeric_limits<double>::max() );
124 mCircle2X->setClearValue( 0.0 );
125 connect( mCircle2X, &QgsDoubleSpinBox::textEdited, this, [ = ]() { mCircle2Digitize->setChecked( false ); } );
126 layout->addWidget( mCircle2X, 5, 1 );
127
128 label = new QLabel( QStringLiteral( "y" ), toolWidget );
129 layout->addWidget( label, 6, 0 );
130
131 mCircle2Y = new QgsDoubleSpinBox( toolWidget );
132 mCircle2Y->setToolTip( tr( "Y coordinate" ) );
133 mCircle2Y->setMinimum( std::numeric_limits<double>::lowest() );
134 mCircle2Y->setMaximum( std::numeric_limits<double>::max() );
135 mCircle2Y->setClearValue( 0.0 );
136 connect( mCircle2Y, &QgsDoubleSpinBox::textEdited, this, [ = ]() { mCircle2Digitize->setChecked( false ); } );
137 layout->addWidget( mCircle2Y, 6, 1 );
138
139 label = new QLabel( QStringLiteral( "d" ), toolWidget );
140 layout->addWidget( label, 7, 0 );
141
142 mCircle2Distance = new QgsDoubleSpinBox( toolWidget );
143 mCircle1Distance->setToolTip( tr( "Distance" ) );
144 mCircle2Distance->setMinimum( 0 );
145 mCircle2Distance->setMaximum( std::numeric_limits<double>::max() );
146 layout->addWidget( mCircle2Distance, 7, 1 );
147
148 connect( mCircle1X, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } );
149 connect( mCircle1Y, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } );
150 connect( mCircle1Distance, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } );
151 connect( mCircle2X, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } );
152 connect( mCircle2Y, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } );
153 connect( mCircle2Distance, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double ) { processParameters(); } );
154
155
156 bool focusOnCircle2 = false;
157 if ( mCadDockWidget )
158 {
159 if ( mCadDockWidget->constraintDistance()->isLocked() )
160 {
161 QgsPoint point = mCadDockWidget->previousPointV2();
162 if ( !point.isEmpty() )
163 {
164 whileBlocking( mCircle1Distance )->setValue( mCadDockWidget->constraintDistance()->value() );
165 whileBlocking( mCircle1X )->setValue( point.x() );
166 whileBlocking( mCircle1Y )->setValue( point.y() );
167 mP1 = point;
168 focusOnCircle2 = true;
169
170 mCadDockWidget->toggleConstraintDistance();
171 }
172 }
173 }
174
175 if ( focusOnCircle2 )
176 {
177 mCircle2Digitize->setChecked( true );
178 }
179 else
180 {
181 mCircle1Digitize->setChecked( true );
182 }
183
184 toolWidget->installEventFilter( this );
185
186 mToolWidget = toolWidget;
187 return toolWidget;
188}
189
190void QgsAdvancedDigitizingCirclesIntersectionTool::processParameters()
191{
192 mP1 = QgsPointXY();
193 mP2 = QgsPointXY();
194 QgsGeometryUtils::circleCircleIntersections( QgsPointXY( mCircle1X->value(), mCircle1Y->value() ), mCircle1Distance->value(),
195 QgsPointXY( mCircle2X->value(), mCircle2Y->value() ), mCircle2Distance->value(),
196 mP1, mP2 );
197 emit paintRequested();
198}
199
201{
202 if ( mCircle1Digitize->isChecked() )
203 {
204 mCircle1X->setValue( event->mapPoint().x() );
205 mCircle1Y->setValue( event->mapPoint().y() );
206 }
207 else if ( mCircle2Digitize->isChecked() )
208 {
209 mCircle2X->setValue( event->mapPoint().x() );
210 mCircle2Y->setValue( event->mapPoint().y() );
211 }
212
213 if ( !mP1.isEmpty() )
214 {
215 mP1Closest = QgsGeometryUtils::distance2D( QgsPoint( mP1 ), QgsPoint( event->mapPoint() ) ) < QgsGeometryUtils::distance2D( QgsPoint( mP2 ), QgsPoint( event->mapPoint() ) );
216 event->setMapPoint( mP1Closest ? mP1 : mP2 );
217 }
218 else
219 {
220 event->setAccepted( false );
221 }
222}
223
225{
226 if ( event->button() == Qt::RightButton )
227 {
228 deleteLater();
229 mCadDockWidget->updateCadPaintItem();
230 event->setAccepted( false );
231 return;
232 }
233
234 if ( mCircle1Digitize->isChecked() )
235 {
236 mCircle1X->setValue( event->mapPoint().x() );
237 mCircle1Y->setValue( event->mapPoint().y() );
238 mCircle1Digitize->setChecked( false );
239 mCircle1Distance->setFocus();
240 mCircle1Distance->selectAll();
241 event->setAccepted( false );
242 return;
243 }
244 else if ( mCircle2Digitize->isChecked() )
245 {
246 mCircle2X->setValue( event->mapPoint().x() );
247 mCircle2Y->setValue( event->mapPoint().y() );
248 mCircle2Digitize->setChecked( false );
249 mCircle2Distance->setFocus();
250 mCircle2Distance->selectAll();
251 event->setAccepted( false );
252 return;
253 }
254
255 if ( !mP1.isEmpty() )
256 {
257 mP1Closest = QgsGeometryUtils::distance2D( QgsPoint( mP1 ), QgsPoint( event->mapPoint() ) ) < QgsGeometryUtils::distance2D( QgsPoint( mP2 ), QgsPoint( event->mapPoint() ) );
258 event->setMapPoint( mP1Closest ? mP1 : mP2 );
259 deleteLater();
260 return;
261 }
262
263 event->setAccepted( false );
264}
265
266void QgsAdvancedDigitizingCirclesIntersectionTool::drawCircle( QPainter *painter, double x, double y, double distance )
267{
268 painter->setPen( QPen( QColor( 0, 127, 0, 200 ), 2 ) );
269 painter->setBrush( Qt::NoBrush );
270
272 painter->drawLine( QLineF( x - 3, y - 3, x + 3, y + 3 ) );
273 painter->drawLine( QLineF( x - 3, y + 3, x + 3, y - 3 ) );
274
275 painter->setPen( QPen( QColor( 0, 127, 0, 150 ), 1, Qt::DashLine ) );
276 distance = distance / mapCanvas()->mapSettings().mapUnitsPerPixel();
277 painter->drawEllipse( QRectF( x - distance, y - distance, distance * 2, distance * 2 ) );
278}
279
280void QgsAdvancedDigitizingCirclesIntersectionTool::drawCandidate( QPainter *painter, double x, double y, bool closest )
281{
282 if ( closest )
283 {
284 painter->setPen( QPen( QColor( 127, 0, 0, 222 ), 4 ) );
285 }
286 else
287 {
288 painter->setPen( QPen( QColor( 0, 127, 0, 222 ), 2 ) );
289 }
290
292 double size = closest ? 5 : 3;
293 painter->drawRect( QRectF( x - size, y - size, size * 2, size * 2 ) );
294}
295
297{
298 painter->save();
299
300 drawCircle( painter, mCircle1X->value(), mCircle1Y->value(), mCircle1Distance->value() );
301 drawCircle( painter, mCircle2X->value(), mCircle2Y->value(), mCircle2Distance->value() );
302
303 if ( !mP1.isEmpty() )
304 {
305 if ( mP1Closest )
306 {
307 drawCandidate( painter, mP2.x(), mP2.y(), false );
308 drawCandidate( painter, mP1.x(), mP1.y(), true );
309 }
310 else
311 {
312 drawCandidate( painter, mP1.x(), mP1.y(), false );
313 drawCandidate( painter, mP2.x(), mP2.y(), true );
314 }
315 }
316
317 painter->restore();
318}
319
320bool QgsAdvancedDigitizingCirclesIntersectionTool::eventFilter( QObject *obj, QEvent *event )
321{
322 if ( event->type() == QEvent::ShortcutOverride || event->type() == QEvent::KeyPress )
323 {
324 if ( QKeyEvent *keyEvent = dynamic_cast<QKeyEvent *>( event ) )
325 {
326 if ( keyEvent->key() == Qt::Key_Escape )
327 {
328 deleteLater();
329 mCadDockWidget->updateCadPaintItem();
330 }
331 }
332 }
333
334 return QObject::eventFilter( obj, event );
335}
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:737
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:5722