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