QGIS API Documentation 3.29.0-Master (8c80f25a4f)
qgsadvanceddigitizingcanvasitem.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsadvanceddigitizingcanvasitem.cpp - map canvas item for CAD tools
3 ----------------------
4 begin : October 2014
5 copyright : (C) Denis Rouzaud
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 <QPainter>
17
20#include "qgsmapcanvas.h"
21#include "qgscadutils.h"
22
23
25 : QgsMapCanvasItem( canvas )
26 , mLockedPen( QPen( QColor( 0, 127, 0, 255 ), 1, Qt::DashLine ) )
27 , mConstruction1Pen( QPen( QColor( 127, 127, 127, 150 ), 1, Qt::DashLine ) )
28 , mConstruction2Pen( QPen( QColor( 127, 127, 127, 255 ), 1, Qt::DashLine ) )
29 , mSnapPen( QPen( QColor( 127, 0, 0, 150 ), 1 ) )
30 , mSnapLinePen( QPen( QColor( 127, 0, 0, 150 ), 1, Qt::DashLine ) )
31 , mCursorPen( QPen( QColor( 127, 127, 127, 255 ), 1 ) )
32 , mAdvancedDigitizingDockWidget( cadDockWidget )
33{
34}
35
37{
38 if ( !mAdvancedDigitizingDockWidget->cadEnabled() )
39 return;
40
41 // Use visible polygon rather than extent to properly handle rotated maps
42 QPolygonF mapPoly = mMapCanvas->mapSettings().visiblePolygon();
43 const double canvasWidth = QLineF( mapPoly[0], mapPoly[1] ).length();
44 const double canvasHeight = QLineF( mapPoly[0], mapPoly[3] ).length();
45
46 const int nPoints = mAdvancedDigitizingDockWidget->pointsCount();
47 if ( !nPoints )
48 return;
49
50 bool previousPointExist, penulPointExist;
51 const QgsPointXY curPoint = mAdvancedDigitizingDockWidget->currentPointV2();
52 const QgsPointXY prevPoint = mAdvancedDigitizingDockWidget->previousPointV2( &previousPointExist );
53 const QgsPointXY penulPoint = mAdvancedDigitizingDockWidget->penultimatePointV2( &penulPointExist );
54 const bool snappedToVertex = mAdvancedDigitizingDockWidget->snappedToVertex();
55 const QList<QgsPointXY> snappedSegment = mAdvancedDigitizingDockWidget->snappedSegment();
56 const bool hasSnappedSegment = snappedSegment.count() == 2;
57
58 const bool curPointExist = mapPoly.containsPoint( curPoint.toQPointF(), Qt::OddEvenFill );
59
60 const double mupp = mMapCanvas->getCoordinateTransform()->mapUnitsPerPixel();
61 if ( mupp == 0 )
62 return;
63
64 const double canvasRotationRad = mMapCanvas->rotation() * M_PI / 180;
65 const double canvasDiagonalDimension = ( canvasWidth + canvasHeight ) / mupp ;
66
67 QPointF curPointPix, prevPointPix, penulPointPix, snapSegmentPix1, snapSegmentPix2;
68
69 if ( curPointExist )
70 {
71 curPointPix = toCanvasCoordinates( curPoint );
72 }
73 if ( previousPointExist )
74 {
75 prevPointPix = toCanvasCoordinates( prevPoint );
76 }
77 if ( penulPointExist )
78 {
79 penulPointPix = toCanvasCoordinates( penulPoint );
80 }
81 if ( hasSnappedSegment )
82 {
83 snapSegmentPix1 = toCanvasCoordinates( snappedSegment[0] );
84 snapSegmentPix2 = toCanvasCoordinates( snappedSegment[1] );
85 }
86
87 painter->setRenderHint( QPainter::Antialiasing );
88 painter->setCompositionMode( QPainter::CompositionMode_Difference );
89
90 // Draw point snap
91 if ( curPointExist && snappedToVertex )
92 {
93 painter->setPen( mSnapPen );
94 painter->drawEllipse( curPointPix, 10, 10 );
95 }
96
97 // Draw segment snap
98 if ( hasSnappedSegment && !snappedToVertex )
99 {
100 painter->setPen( mSnapPen );
101 painter->drawLine( snapSegmentPix1, snapSegmentPix2 );
102
103 if ( curPointExist )
104 {
105 painter->setPen( mSnapLinePen );
106 painter->drawLine( snapSegmentPix1, curPointPix );
107 }
108 }
109
110 // Draw segment par/per input
111 if ( mAdvancedDigitizingDockWidget->betweenLineConstraint() != Qgis::BetweenLineConstraint::NoConstraint && hasSnappedSegment )
112 {
113 painter->setPen( mConstruction2Pen );
114 painter->drawLine( snapSegmentPix1, snapSegmentPix2 );
115 }
116
117 // Draw angle
118 if ( nPoints > 1 )
119 {
120 double a0, a;
121 if ( mAdvancedDigitizingDockWidget->constraintAngle()->relative() && nPoints > 2 )
122 {
123 a0 = std::atan2( -( prevPoint.y() - penulPoint.y() ), prevPoint.x() - penulPoint.x() );
124 }
125 else
126 {
127 a0 = 0;
128 }
129 if ( mAdvancedDigitizingDockWidget->constraintAngle()->isLocked() )
130 {
131 a = a0 - mAdvancedDigitizingDockWidget->constraintAngle()->value() * M_PI / 180;
132 }
133 else
134 {
135 a = std::atan2( -( curPoint.y() - prevPoint.y() ), curPoint.x() - prevPoint.x() );
136 }
137
138 a0 += canvasRotationRad;
139 a += canvasRotationRad;
140
141 painter->setPen( mConstruction2Pen );
142 painter->drawArc( QRectF( prevPointPix.x() - 20,
143 prevPointPix.y() - 20,
144 40, 40 ),
145 static_cast<int>( 16 * -a0 * 180 / M_PI ),
146 static_cast<int>( 16 * ( a0 - a ) * 180 / M_PI ) );
147 painter->drawLine( prevPointPix,
148 prevPointPix + 60 * QPointF( std::cos( a0 ), std::sin( a0 ) ) );
149
150
151 if ( mAdvancedDigitizingDockWidget->constraintAngle()->isLocked() )
152 {
153 painter->setPen( mLockedPen );
154 const double canvasPadding = QLineF( prevPointPix, curPointPix ).length();
155 painter->drawLine( prevPointPix + ( canvasPadding - canvasDiagonalDimension ) * QPointF( std::cos( a ), std::sin( a ) ),
156 prevPointPix + ( canvasPadding + canvasDiagonalDimension ) * QPointF( std::cos( a ), std::sin( a ) ) );
157 }
158 }
159
160 // Draw distance
161 if ( nPoints > 1 && mAdvancedDigitizingDockWidget->constraintDistance()->isLocked() )
162 {
163 painter->setPen( mLockedPen );
164 const double r = mAdvancedDigitizingDockWidget->constraintDistance()->value() / mupp;
165 painter->drawEllipse( prevPointPix, r, r );
166 }
167
168 // Draw x
169 if ( mAdvancedDigitizingDockWidget->constraintX()->isLocked() )
170 {
171 double x = 0.0;
172 bool draw = true;
173 painter->setPen( mLockedPen );
174 if ( mAdvancedDigitizingDockWidget->constraintX()->relative() )
175 {
176 if ( nPoints > 1 )
177 {
178 x = mAdvancedDigitizingDockWidget->constraintX()->value() + prevPoint.x();
179 }
180 else
181 {
182 draw = false;
183 }
184 }
185 else
186 {
187 x = mAdvancedDigitizingDockWidget->constraintX()->value();
188 }
189 if ( draw )
190 {
191 painter->drawLine( toCanvasCoordinates( QgsPointXY( x, mapPoly[0].y() ) ) - canvasDiagonalDimension * QPointF( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) ),
192 toCanvasCoordinates( QgsPointXY( x, mapPoly[0].y() ) ) + canvasDiagonalDimension * QPointF( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) ) );
193 }
194 }
195
196 // Draw y
197 if ( mAdvancedDigitizingDockWidget->constraintY()->isLocked() )
198 {
199 double y = 0.0;
200 bool draw = true;
201 painter->setPen( mLockedPen );
202 if ( mAdvancedDigitizingDockWidget->constraintY()->relative() )
203 {
204 if ( nPoints > 1 )
205 {
206 y = mAdvancedDigitizingDockWidget->constraintY()->value() + prevPoint.y();
207 }
208 else
209 {
210 draw = false;
211 }
212 }
213 else
214 {
215 y = mAdvancedDigitizingDockWidget->constraintY()->value();
216 }
217 if ( draw )
218 {
219 painter->drawLine( toCanvasCoordinates( QgsPointXY( mapPoly[0].x(), y ) ) - canvasDiagonalDimension * QPointF( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) ),
220 toCanvasCoordinates( QgsPointXY( mapPoly[0].x(), y ) ) + canvasDiagonalDimension * QPointF( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) ) );
221
222 }
223 }
224
225 // Draw constr
226 if ( mAdvancedDigitizingDockWidget->betweenLineConstraint() == Qgis::BetweenLineConstraint::NoConstraint )
227 {
228 if ( curPointExist && previousPointExist )
229 {
230 painter->setPen( mConstruction2Pen );
231 painter->drawLine( prevPointPix, curPointPix );
232 }
233
234 if ( previousPointExist && penulPointExist )
235 {
236 painter->setPen( mConstruction1Pen );
237 painter->drawLine( penulPointPix, prevPointPix );
238 }
239 }
240
241 if ( curPointExist )
242 {
243 painter->setPen( mCursorPen );
244 painter->drawLine( curPointPix + QPointF( -5, -5 ),
245 curPointPix + QPointF( +5, +5 ) );
246 painter->drawLine( curPointPix + QPointF( -5, +5 ),
247 curPointPix + QPointF( +5, -5 ) );
248 }
249
250 auto lineExtensionSide = mAdvancedDigitizingDockWidget->lineExtensionSide();
251 if ( mAdvancedDigitizingDockWidget->constraintLineExtension()->isLocked() &&
252 lineExtensionSide != Qgis::LineExtensionSide::NoVertex &&
253 !mAdvancedDigitizingDockWidget->lockedSnapVertices().isEmpty() )
254 {
255 painter->setPen( mLockedPen );
256
257 const QgsPointLocator::Match snap = mAdvancedDigitizingDockWidget->lockedSnapVertices().last();
258 const QPointF snappedPoint = toCanvasCoordinates( snap.point() );
259
260 const QgsFeature feature = snap.layer()->getFeature( snap.featureId() );
261 const QgsGeometry geom = feature.geometry();
262
263 QgsPoint vertex;
264 if ( lineExtensionSide == Qgis::LineExtensionSide::BeforeVertex )
265 {
266 vertex = geom.vertexAt( snap.vertexIndex() - 1 );
267 }
268 else
269 {
270 vertex = geom.vertexAt( snap.vertexIndex() + 1 );
271 }
272
273 if ( !vertex.isEmpty() )
274 {
275 const QPointF point = toCanvasCoordinates( vertex );
276 const double angle = std::atan2( snappedPoint.y() - point.y(), snappedPoint.x() - point.x() );
277
278 const double canvasPadding = QLineF( snappedPoint, curPointPix ).length();
279 painter->drawLine( snappedPoint + ( canvasPadding - canvasDiagonalDimension ) * QPointF( std::cos( angle ), std::sin( angle ) ),
280 snappedPoint + ( canvasPadding + canvasDiagonalDimension ) * QPointF( std::cos( angle ), std::sin( angle ) ) );
281 }
282
283 }
284
285 if ( mAdvancedDigitizingDockWidget->constraintXyVertex()->isLocked() )
286 {
287 painter->setPen( mLockedPen );
288
289 double coordinateExtension = mAdvancedDigitizingDockWidget->softLockX();
290 if ( coordinateExtension != std::numeric_limits<double>::quiet_NaN() )
291 {
292 const QgsPointXY point( coordinateExtension, mapPoly[0].y() );
293 const QPointF rotation( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) );
294 painter->drawLine( toCanvasCoordinates( point ) - canvasDiagonalDimension * rotation,
295 toCanvasCoordinates( point ) + canvasDiagonalDimension * rotation );
296 }
297
298 coordinateExtension = mAdvancedDigitizingDockWidget->softLockY();
299 if ( coordinateExtension != std::numeric_limits<double>::quiet_NaN() )
300 {
301 const QgsPointXY point( mapPoly[0].x(), coordinateExtension );
302 const QPointF rotation( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) );
303 painter->drawLine( toCanvasCoordinates( point ) - canvasDiagonalDimension * rotation,
304 toCanvasCoordinates( point ) + canvasDiagonalDimension * rotation );
305 }
306 }
307
308 painter->setPen( mCursorPen );
309
310 const QList< QgsPointLocator::Match > lockedSnapVertices = mAdvancedDigitizingDockWidget->lockedSnapVertices();
311 for ( QgsPointLocator::Match snapMatch : lockedSnapVertices )
312 {
313 const QgsPointXY point = snapMatch.point();
314 const QPointF canvasPoint = toCanvasCoordinates( point );
315
316 painter->drawLine( canvasPoint + QPointF( 5, 5 ),
317 canvasPoint - QPointF( 5, 5 ) );
318 painter->drawLine( canvasPoint + QPointF( -5, 5 ),
319 canvasPoint - QPointF( -5, 5 ) );
320 }
321
322 if ( !lockedSnapVertices.isEmpty() )
323 {
324 const QgsPointXY point = lockedSnapVertices.last().point();
325 const QPointF canvasPoint = toCanvasCoordinates( point );
326
327 painter->drawLine( canvasPoint + QPointF( 0, 5 ),
328 canvasPoint - QPointF( 0, 5 ) );
329 painter->drawLine( canvasPoint + QPointF( 5, 0 ),
330 canvasPoint - QPointF( 5, 0 ) );
331 }
332}
333
335{
336 // Use visible polygon rather than extent to properly handle rotated maps
337 QPolygonF mapPoly = mMapCanvas->mapSettings().visiblePolygon();
338 const double canvasWidth = QLineF( mapPoly[0], mapPoly[1] ).length();
339 const double canvasHeight = QLineF( mapPoly[0], mapPoly[3] ).length();
340 const QgsRectangle mapRect = QgsRectangle( mapPoly[0],
342 mapPoly[0].x() + canvasWidth,
343 mapPoly[0].y() - canvasHeight
344 )
345 );
346 if ( rect() != mapRect )
347 setRect( mapRect );
348}
@ NoConstraint
No additional constraint.
@ NoVertex
Don't lock to vertex.
@ BeforeVertex
Lock to previous vertex.
void paint(QPainter *painter) override
function to be implemented by derived classes
void updatePosition() override
called on changed extent or resize event to update position of the item
QgsAdvancedDigitizingCanvasItem(QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget)
bool isLocked() const
Is any kind of lock mode enabled.
double value() const
The value of the constraint.
bool relative() const
Is the constraint in relative mode.
The QgsAdvancedDigitizingDockWidget class is a dockable widget used to handle the CAD tools on top of...
double softLockY() const
Returns the Y value of the Y soft lock. The value is NaN is the constraint isn't magnetized to a line...
bool cadEnabled() const
determines if CAD tools are enabled or if map tools behaves "nomally"
int pointsCount() const
The number of points in the CAD point helper list.
QList< QgsPointXY > snappedSegment() const
Snapped to a segment.
const CadConstraint * constraintLineExtension() const
Returns the CadConstraint.
bool snappedToVertex() const
Is it snapped to a vertex.
QList< QgsPointLocator::Match > lockedSnapVertices() const
Returns the snap matches whose vertices have been locked.
QgsPoint previousPointV2(bool *exists=nullptr) const
The previous point.
QgsPoint penultimatePointV2(bool *exists=nullptr) const
The penultimate point.
const CadConstraint * constraintXyVertex() const
Returns the CadConstraint.
const CadConstraint * constraintY() const
Returns the CadConstraint on the Y coordinate.
double softLockX() const
Returns the X value of the X soft lock. The value is NaN is the constraint isn't magnetized to a line...
Qgis::BetweenLineConstraint betweenLineConstraint() const
Returns the between line constraints which are used to place perpendicular/parallel segments to snapp...
const CadConstraint * constraintX() const
Returns the CadConstraint on the X coordinate.
QgsPoint currentPointV2(bool *exists=nullptr) const
The last point.
const CadConstraint * constraintAngle() const
Returns the CadConstraint on the angle.
Qgis::LineExtensionSide lineExtensionSide() const
Returns on which side of the constraint line extension point, the line was created.
const CadConstraint * constraintDistance() const
Returns the CadConstraint on the distance.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:164
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
An abstract class for items that can be placed on the map canvas.
QgsRectangle rect() const
returns canvas item rectangle in map units
QPointF toCanvasCoordinates(const QgsPointXY &point) const
transformation from map coordinates to screen coordinates
QgsMapCanvas * mMapCanvas
pointer to map canvas
void setRect(const QgsRectangle &r, bool resetRotation=true)
sets canvas item rectangle in map units
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:90
double rotation() const
Gets the current map canvas rotation in clockwise degrees.
const QgsMapToPixel * getCoordinateTransform()
Gets the current coordinate transform.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QPolygonF visiblePolygon() const
Returns the visible area as a polygon (may be rotated)
double mapUnitsPerPixel() const
Returns the current map units per pixel.
A class to represent a 2D point.
Definition: qgspointxy.h:59
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
QPointF toQPointF() const
Converts a point to a QPointF.
Definition: qgspointxy.h:169
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
Definition: qgspoint.cpp:767
A rectangle specified with double values.
Definition: qgsrectangle.h:42
QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
QgsFeatureId featureId() const
The id of the feature to which the snapped geometry belongs.
QgsVectorLayer * layer() const
The vector layer where the snap occurred.
QgsPointXY point() const
for vertex / edge match coords depending on what class returns it (geom.cache: layer coords,...
int vertexIndex() const
for vertex / edge match (first vertex of the edge)