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