QGIS API Documentation 3.99.0-Master (2fe06baccd8)
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
17
18#include "qgsapplication.h"
19#include "qgsdoublespinbox.h"
20#include "qgsmapcanvas.h"
21
22#include <QEnterEvent>
23#include <QLocale>
24#include <QMouseEvent>
25
26#include "moc_qgsadvanceddigitizingtools.cpp"
27
29 : QObject( canvas ? canvas->viewport() : nullptr )
30 , mMapCanvas( canvas )
32{
33}
34
39
41{
42 if ( mToolWidget )
43 {
44 mToolWidget->deleteLater();
45 }
46}
47
49{
50 QWidget *toolWidget = new QWidget();
51
52 QGridLayout *layout = new QGridLayout( toolWidget );
53 layout->setContentsMargins( 0, 0, 0, 0 );
54 toolWidget->setLayout( layout );
55
56 QLabel *label = new QLabel( tr( "Circle #1" ), toolWidget );
57 layout->addWidget( label, 0, 0, 1, 3 );
58
59 mCircle1Digitize = new QToolButton( toolWidget );
60 mCircle1Digitize->setCheckable( true );
61 mCircle1Digitize->setChecked( false );
62 mCircle1Digitize->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionMapIdentification.svg" ) ) );
63 connect( mCircle1Digitize, &QAbstractButton::toggled, this, [this]( bool checked ) {
64 if ( checked )
65 {
66 mCircle2Digitize->setChecked( false );
67 }
68 } );
69 layout->addWidget( mCircle1Digitize, 1, 2, 2, 1 );
70
71 label = new QLabel( QStringLiteral( "x" ), toolWidget );
72 layout->addWidget( label, 1, 0 );
73
74 mCircle1X = new QgsDoubleSpinBox( toolWidget );
75 mCircle1X->setToolTip( tr( "X coordinate" ) );
76 mCircle1X->setMinimum( std::numeric_limits<double>::lowest() );
77 mCircle1X->setMaximum( std::numeric_limits<double>::max() );
78 mCircle1X->setDecimals( mCadDockWidget->constraintX()->precision() );
79 mCircle1X->setClearValue( 0.0 );
80 connect( mCircle1X, &QgsDoubleSpinBox::textEdited, this, [this]() { mCircle1Digitize->setChecked( false ); } );
81 layout->addWidget( mCircle1X, 1, 1 );
82
83 label = new QLabel( QStringLiteral( "y" ), toolWidget );
84 layout->addWidget( label, 2, 0 );
85
86 mCircle1Y = new QgsDoubleSpinBox( toolWidget );
87 mCircle1Y->setToolTip( tr( "Y coordinate" ) );
88 mCircle1Y->setMinimum( std::numeric_limits<double>::lowest() );
89 mCircle1Y->setMaximum( std::numeric_limits<double>::max() );
90 mCircle1Y->setDecimals( mCadDockWidget->constraintY()->precision() );
91 mCircle1Y->setClearValue( 0.0 );
92 connect( mCircle1Y, &QgsDoubleSpinBox::textEdited, this, [this]() { mCircle1Digitize->setChecked( false ); } );
93 layout->addWidget( mCircle1Y, 2, 1 );
94
95 label = new QLabel( QStringLiteral( "d" ), toolWidget );
96 layout->addWidget( label, 3, 0 );
97
98 mCircle1Distance = new QgsDoubleSpinBox( toolWidget );
99 mCircle1Distance->setToolTip( tr( "Distance" ) );
100 mCircle1Distance->setMinimum( 0 );
101 mCircle1Distance->setMaximum( std::numeric_limits<double>::max() );
102 mCircle1Distance->setDecimals( mCadDockWidget->constraintX()->precision() );
103 connect( mCircle1Distance, &QgsDoubleSpinBox::returnPressed, this, [this]() { mCircle2Digitize->setChecked( true ); } );
104 layout->addWidget( mCircle1Distance, 3, 1 );
105
106 label = new QLabel( tr( "Circle #2" ), toolWidget );
107 layout->addWidget( label, 4, 0, 1, 3 );
108
109 mCircle2Digitize = new QToolButton( toolWidget );
110 mCircle2Digitize->setCheckable( true );
111 mCircle2Digitize->setChecked( false );
112 mCircle2Digitize->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionMapIdentification.svg" ) ) );
113 connect( mCircle2Digitize, &QAbstractButton::toggled, this, [this]( bool checked ) {
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, [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, [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, [this]( double ) { processParameters(); } );
156 connect( mCircle1Y, static_cast<void ( QgsDoubleSpinBox::* )( double )>( &QgsDoubleSpinBox::valueChanged ), this, [this]( double ) { processParameters(); } );
157 connect( mCircle1Distance, static_cast<void ( QgsDoubleSpinBox::* )( double )>( &QgsDoubleSpinBox::valueChanged ), this, [this]( double ) { processParameters(); } );
158 connect( mCircle2X, static_cast<void ( QgsDoubleSpinBox::* )( double )>( &QgsDoubleSpinBox::valueChanged ), this, [this]( double ) { processParameters(); } );
159 connect( mCircle2Y, static_cast<void ( QgsDoubleSpinBox::* )( double )>( &QgsDoubleSpinBox::valueChanged ), this, [this]( double ) { processParameters(); } );
160 connect( mCircle2Distance, static_cast<void ( QgsDoubleSpinBox::* )( double )>( &QgsDoubleSpinBox::valueChanged ), this, [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(), QgsPointXY( mCircle2X->value(), mCircle2Y->value() ), mCircle2Distance->value(), mP1, mP2 );
202 emit paintRequested();
203}
204
206{
207 if ( mCircle1Digitize->isChecked() )
208 {
209 mCircle1X->setValue( event->mapPoint().x() );
210 mCircle1Y->setValue( event->mapPoint().y() );
211 }
212 else if ( mCircle2Digitize->isChecked() )
213 {
214 mCircle2X->setValue( event->mapPoint().x() );
215 mCircle2Y->setValue( event->mapPoint().y() );
216 }
217
218 if ( !mP1.isEmpty() )
219 {
220 mP1Closest = QgsGeometryUtils::distance2D( QgsPoint( mP1 ), QgsPoint( event->mapPoint() ) ) < QgsGeometryUtils::distance2D( QgsPoint( mP2 ), QgsPoint( event->mapPoint() ) );
221 event->setMapPoint( mP1Closest ? mP1 : mP2 );
222 }
223 else
224 {
225 event->setAccepted( false );
226 }
227}
228
230{
231 if ( event->button() == Qt::RightButton )
232 {
233 deleteLater();
234 mCadDockWidget->updateCadPaintItem();
235 event->setAccepted( false );
236 return;
237 }
238
239 if ( mCircle1Digitize->isChecked() )
240 {
241 mCircle1X->setValue( event->mapPoint().x() );
242 mCircle1Y->setValue( event->mapPoint().y() );
243 mCircle1Digitize->setChecked( false );
244 mCircle1Distance->setFocus();
245 mCircle1Distance->selectAll();
246 event->setAccepted( false );
247 return;
248 }
249 else if ( mCircle2Digitize->isChecked() )
250 {
251 mCircle2X->setValue( event->mapPoint().x() );
252 mCircle2Y->setValue( event->mapPoint().y() );
253 mCircle2Digitize->setChecked( false );
254 mCircle2Distance->setFocus();
255 mCircle2Distance->selectAll();
256 event->setAccepted( false );
257 return;
258 }
259
260 if ( !mP1.isEmpty() )
261 {
262 mP1Closest = QgsGeometryUtils::distance2D( QgsPoint( mP1 ), QgsPoint( event->mapPoint() ) ) < QgsGeometryUtils::distance2D( QgsPoint( mP2 ), QgsPoint( event->mapPoint() ) );
263 event->setMapPoint( mP1Closest ? mP1 : mP2 );
264 deleteLater();
265 return;
266 }
267
268 event->setAccepted( false );
269}
270
271void QgsAdvancedDigitizingCirclesIntersectionTool::drawCircle( QPainter *painter, double x, double y, double distance )
272{
273 painter->setPen( QPen( QColor( 0, 127, 0, 200 ), 2 ) );
274 painter->setBrush( Qt::NoBrush );
275
277 painter->drawLine( QLineF( x - 3, y - 3, x + 3, y + 3 ) );
278 painter->drawLine( QLineF( x - 3, y + 3, x + 3, y - 3 ) );
279
280 painter->setPen( QPen( QColor( 0, 127, 0, 150 ), 1, Qt::DashLine ) );
281 distance = distance / mapCanvas()->mapSettings().mapUnitsPerPixel();
282 painter->drawEllipse( QRectF( x - distance, y - distance, distance * 2, distance * 2 ) );
283}
284
285void QgsAdvancedDigitizingCirclesIntersectionTool::drawCandidate( QPainter *painter, double x, double y, bool closest )
286{
287 if ( closest )
288 {
289 painter->setPen( QPen( QColor( 127, 0, 0, 222 ), 4 ) );
290 }
291 else
292 {
293 painter->setPen( QPen( QColor( 0, 127, 0, 222 ), 2 ) );
294 }
295
297 double size = closest ? 5 : 3;
298 painter->drawRect( QRectF( x - size, y - size, size * 2, size * 2 ) );
299}
300
302{
303 painter->save();
304
305 drawCircle( painter, mCircle1X->value(), mCircle1Y->value(), mCircle1Distance->value() );
306 drawCircle( painter, mCircle2X->value(), mCircle2Y->value(), mCircle2Distance->value() );
307
308 if ( !mP1.isEmpty() )
309 {
310 if ( mP1Closest )
311 {
312 drawCandidate( painter, mP2.x(), mP2.y(), false );
313 drawCandidate( painter, mP1.x(), mP1.y(), true );
314 }
315 else
316 {
317 drawCandidate( painter, mP1.x(), mP1.y(), false );
318 drawCandidate( painter, mP2.x(), mP2.y(), true );
319 }
320 }
321
322 painter->restore();
323}
324
325bool QgsAdvancedDigitizingCirclesIntersectionTool::eventFilter( QObject *obj, QEvent *event )
326{
327 if ( event->type() == QEvent::ShortcutOverride || event->type() == QEvent::KeyPress )
328 {
329 if ( QKeyEvent *keyEvent = dynamic_cast<QKeyEvent *>( event ) )
330 {
331 if ( keyEvent->key() == Qt::Key_Escape )
332 {
333 deleteLater();
334 mCadDockWidget->updateCadPaintItem();
335 }
336 }
337 }
338
339 return QObject::eventFilter( obj, event );
340}
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.
A dockable widget used to handle the CAD tools on top of a selection of map tools.
QgsAdvancedDigitizingDockWidget * cadDockWidget() const
Returns the advanced digitizing widget associated with the tool.
QgsMapCanvas * mapCanvas() const
Returns the map canvas associated with the tool.
void paintRequested()
Requests a new painting event to the advanced digitizing canvas item.
QPointer< QgsAdvancedDigitizingDockWidget > mCadDockWidget
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 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 mouse event which is the result of a user interaction with 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.
Represents a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
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:739
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:6511