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