QGIS API Documentation 4.3.0-Master (16eddb2670e)
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#include "qgspainting.h"
22
23#include <QEnterEvent>
24#include <QLocale>
25#include <QMouseEvent>
26#include <QString>
27
28#include "moc_qgsadvanceddigitizingtools.cpp"
29
30using namespace Qt::StringLiterals;
31
33 : QObject( canvas ? canvas->viewport() : nullptr )
34 , mMapCanvas( canvas )
36{}
37
41
43{
44 if ( mToolWidget )
45 {
46 mToolWidget->deleteLater();
47 }
48}
49
51{
52 QWidget *toolWidget = new QWidget();
53
54 QGridLayout *layout = new QGridLayout( toolWidget );
55 layout->setContentsMargins( 0, 0, 0, 0 );
56 toolWidget->setLayout( layout );
57
58 QLabel *label = new QLabel( tr( "Circle #1" ), toolWidget );
59 layout->addWidget( label, 0, 0, 1, 3 );
60
61 mCircle1Digitize = new QToolButton( toolWidget );
62 mCircle1Digitize->setCheckable( true );
63 mCircle1Digitize->setChecked( false );
64 mCircle1Digitize->setIcon( QgsApplication::getThemeIcon( u"/mActionMapIdentification.svg"_s ) );
65 connect( mCircle1Digitize, &QAbstractButton::toggled, this, [this]( bool checked ) {
66 if ( checked )
67 {
68 mCircle2Digitize->setChecked( false );
69 }
70 } );
71 layout->addWidget( mCircle1Digitize, 1, 2, 2, 1 );
72
73 label = new QLabel( u"x"_s, toolWidget );
74 layout->addWidget( label, 1, 0 );
75
76 mCircle1X = new QgsDoubleSpinBox( toolWidget );
77 mCircle1X->setToolTip( tr( "X coordinate" ) );
78 mCircle1X->setMinimum( std::numeric_limits<double>::lowest() );
79 mCircle1X->setMaximum( std::numeric_limits<double>::max() );
80 mCircle1X->setDecimals( mCadDockWidget->constraintX()->precision() );
81 mCircle1X->setClearValue( 0.0 );
82 connect( mCircle1X, &QgsDoubleSpinBox::textEdited, this, [this]() { mCircle1Digitize->setChecked( false ); } );
83 layout->addWidget( mCircle1X, 1, 1 );
84
85 label = new QLabel( u"y"_s, toolWidget );
86 layout->addWidget( label, 2, 0 );
87
88 mCircle1Y = new QgsDoubleSpinBox( toolWidget );
89 mCircle1Y->setToolTip( tr( "Y coordinate" ) );
90 mCircle1Y->setMinimum( std::numeric_limits<double>::lowest() );
91 mCircle1Y->setMaximum( std::numeric_limits<double>::max() );
92 mCircle1Y->setDecimals( mCadDockWidget->constraintY()->precision() );
93 mCircle1Y->setClearValue( 0.0 );
94 connect( mCircle1Y, &QgsDoubleSpinBox::textEdited, this, [this]() { mCircle1Digitize->setChecked( false ); } );
95 layout->addWidget( mCircle1Y, 2, 1 );
96
97 label = new QLabel( u"d"_s, toolWidget );
98 layout->addWidget( label, 3, 0 );
99
100 mCircle1Distance = new QgsDoubleSpinBox( toolWidget );
101 mCircle1Distance->setToolTip( tr( "Distance" ) );
102 mCircle1Distance->setMinimum( 0 );
103 mCircle1Distance->setMaximum( std::numeric_limits<double>::max() );
104 mCircle1Distance->setDecimals( mCadDockWidget->constraintX()->precision() );
105 connect( mCircle1Distance, &QgsDoubleSpinBox::returnPressed, this, [this]() { mCircle2Digitize->setChecked( true ); } );
106 layout->addWidget( mCircle1Distance, 3, 1 );
107
108 label = new QLabel( tr( "Circle #2" ), toolWidget );
109 layout->addWidget( label, 4, 0, 1, 3 );
110
111 mCircle2Digitize = new QToolButton( toolWidget );
112 mCircle2Digitize->setCheckable( true );
113 mCircle2Digitize->setChecked( false );
114 mCircle2Digitize->setIcon( QgsApplication::getThemeIcon( u"/mActionMapIdentification.svg"_s ) );
115 connect( mCircle2Digitize, &QAbstractButton::toggled, this, [this]( bool checked ) {
116 if ( checked )
117 {
118 mCircle1Digitize->setChecked( false );
119 }
120 } );
121 layout->addWidget( mCircle2Digitize, 5, 2, 2, 1 );
122
123 label = new QLabel( u"x"_s, toolWidget );
124 layout->addWidget( label, 5, 0 );
125
126 mCircle2X = new QgsDoubleSpinBox( toolWidget );
127 mCircle2X->setToolTip( tr( "X coordinate" ) );
128 mCircle2X->setMinimum( std::numeric_limits<double>::lowest() );
129 mCircle2X->setMaximum( std::numeric_limits<double>::max() );
130 mCircle2X->setDecimals( mCadDockWidget->constraintX()->precision() );
131 mCircle2X->setClearValue( 0.0 );
132 connect( mCircle2X, &QgsDoubleSpinBox::textEdited, this, [this]() { mCircle2Digitize->setChecked( false ); } );
133 layout->addWidget( mCircle2X, 5, 1 );
134
135 label = new QLabel( u"y"_s, toolWidget );
136 layout->addWidget( label, 6, 0 );
137
138 mCircle2Y = new QgsDoubleSpinBox( toolWidget );
139 mCircle2Y->setToolTip( tr( "Y coordinate" ) );
140 mCircle2Y->setMinimum( std::numeric_limits<double>::lowest() );
141 mCircle2Y->setMaximum( std::numeric_limits<double>::max() );
142 mCircle2Y->setDecimals( mCadDockWidget->constraintY()->precision() );
143 mCircle2Y->setClearValue( 0.0 );
144 connect( mCircle2Y, &QgsDoubleSpinBox::textEdited, this, [this]() { mCircle2Digitize->setChecked( false ); } );
145 layout->addWidget( mCircle2Y, 6, 1 );
146
147 label = new QLabel( u"d"_s, toolWidget );
148 layout->addWidget( label, 7, 0 );
149
150 mCircle2Distance = new QgsDoubleSpinBox( toolWidget );
151 mCircle1Distance->setToolTip( tr( "Distance" ) );
152 mCircle2Distance->setMinimum( 0 );
153 mCircle2Distance->setMaximum( std::numeric_limits<double>::max() );
154 mCircle2Distance->setDecimals( mCadDockWidget->constraintX()->precision() );
155 layout->addWidget( mCircle2Distance, 7, 1 );
156
157 connect( mCircle1X, static_cast<void ( QgsDoubleSpinBox::* )( double )>( &QgsDoubleSpinBox::valueChanged ), this, [this]( double ) { processParameters(); } );
158 connect( mCircle1Y, static_cast<void ( QgsDoubleSpinBox::* )( double )>( &QgsDoubleSpinBox::valueChanged ), this, [this]( double ) { processParameters(); } );
159 connect( mCircle1Distance, static_cast<void ( QgsDoubleSpinBox::* )( double )>( &QgsDoubleSpinBox::valueChanged ), this, [this]( double ) { processParameters(); } );
160 connect( mCircle2X, static_cast<void ( QgsDoubleSpinBox::* )( double )>( &QgsDoubleSpinBox::valueChanged ), this, [this]( double ) { processParameters(); } );
161 connect( mCircle2Y, static_cast<void ( QgsDoubleSpinBox::* )( double )>( &QgsDoubleSpinBox::valueChanged ), this, [this]( double ) { processParameters(); } );
162 connect( mCircle2Distance, static_cast<void ( QgsDoubleSpinBox::* )( double )>( &QgsDoubleSpinBox::valueChanged ), this, [this]( double ) { processParameters(); } );
163
164
165 bool focusOnCircle2 = false;
166 if ( mCadDockWidget )
167 {
168 if ( mCadDockWidget->constraintDistance()->isLocked() )
169 {
170 QgsPoint point = mCadDockWidget->previousPointV2();
171 if ( !point.isEmpty() )
172 {
173 whileBlocking( mCircle1Distance )->setValue( mCadDockWidget->constraintDistance()->value() );
174 whileBlocking( mCircle1X )->setValue( point.x() );
175 whileBlocking( mCircle1Y )->setValue( point.y() );
176 mP1 = point;
177 focusOnCircle2 = true;
178
179 mCadDockWidget->toggleConstraintDistance();
180 }
181 }
182 }
183
184 if ( focusOnCircle2 )
185 {
186 mCircle2Digitize->setChecked( true );
187 }
188 else
189 {
190 mCircle1Digitize->setChecked( true );
191 }
192
193 toolWidget->installEventFilter( this );
194
195 mToolWidget = toolWidget;
196 return toolWidget;
197}
198
199void QgsAdvancedDigitizingCirclesIntersectionTool::processParameters()
200{
201 mP1 = QgsPointXY();
202 mP2 = QgsPointXY();
204 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 QgsScopedQPainterState painterState( painter );
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
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.
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:775
double y
Definition qgspoint.h:57
Scoped object for saving and restoring a QPainter object's state.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:7637