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