QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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
22
24 : QgsMapCanvasItem( canvas )
25 , mLockedPen( QPen( QColor( 0, 127, 0, 255 ), 1, Qt::DashLine ) )
26 , mConstruction1Pen( QPen( QColor( 127, 127, 127, 150 ), 1, Qt::DashLine ) )
27 , mConstruction2Pen( QPen( QColor( 127, 127, 127, 255 ), 1, Qt::DashLine ) )
28 , mSnapPen( QPen( QColor( 127, 0, 0, 150 ), 1 ) )
29 , mSnapLinePen( QPen( QColor( 127, 0, 0, 150 ), 1, Qt::DashLine ) )
30 , mCursorPen( QPen( QColor( 127, 127, 127, 255 ), 1 ) )
31 , mAdvancedDigitizingDockWidget( cadDockWidget )
32{
33}
34
36{
37 if ( !mAdvancedDigitizingDockWidget->cadEnabled() )
38 return;
39
40 // Use visible polygon rather than extent to properly handle rotated maps
41 QPolygonF mapPoly = mMapCanvas->mapSettings().visiblePolygon();
42 const double canvasWidth = QLineF( mapPoly[0], mapPoly[1] ).length();
43 const double canvasHeight = QLineF( mapPoly[0], mapPoly[3] ).length();
44
45 const int nPoints = mAdvancedDigitizingDockWidget->pointsCount();
46 if ( !nPoints )
47 return;
48
49 bool previousPointExist, penulPointExist;
50 const QgsPointXY curPoint = mAdvancedDigitizingDockWidget->currentPointV2();
51 const QgsPointXY prevPoint = mAdvancedDigitizingDockWidget->previousPointV2( &previousPointExist );
52 const QgsPointXY penulPoint = mAdvancedDigitizingDockWidget->penultimatePointV2( &penulPointExist );
53 const bool snappedToVertex = mAdvancedDigitizingDockWidget->snappedToVertex();
54 const QList<QgsPointXY> snappedSegment = mAdvancedDigitizingDockWidget->snappedSegment();
55 const bool hasSnappedSegment = snappedSegment.count() == 2;
56
57 const bool curPointExist = mapPoly.containsPoint( curPoint.toQPointF(), Qt::OddEvenFill );
58
59 const double mupp = mMapCanvas->getCoordinateTransform()->mapUnitsPerPixel();
60 if ( mupp == 0 )
61 return;
62
63 const double canvasRotationRad = mMapCanvas->rotation() * M_PI / 180;
64 const double canvasDiagonalDimension = ( canvasWidth + canvasHeight ) / mupp ;
65
66 QPointF curPointPix, prevPointPix, penulPointPix, snapSegmentPix1, snapSegmentPix2;
67
68 if ( curPointExist )
69 {
70 curPointPix = toCanvasCoordinates( curPoint );
71 }
72 if ( previousPointExist )
73 {
74 prevPointPix = toCanvasCoordinates( prevPoint );
75 }
76 if ( penulPointExist )
77 {
78 penulPointPix = toCanvasCoordinates( penulPoint );
79 }
80 if ( hasSnappedSegment )
81 {
82 snapSegmentPix1 = toCanvasCoordinates( snappedSegment[0] );
83 snapSegmentPix2 = toCanvasCoordinates( snappedSegment[1] );
84 }
85
86 painter->setRenderHint( QPainter::Antialiasing );
87 painter->setCompositionMode( QPainter::CompositionMode_Difference );
88
89 // Draw point snap
90 if ( curPointExist && snappedToVertex )
91 {
92 painter->setPen( mSnapPen );
93 painter->drawEllipse( curPointPix, 10, 10 );
94 }
95
96 // Draw segment snap
97 if ( hasSnappedSegment && !snappedToVertex )
98 {
99 painter->setPen( mSnapPen );
100 painter->drawLine( snapSegmentPix1, snapSegmentPix2 );
101
102 if ( curPointExist )
103 {
104 painter->setPen( mSnapLinePen );
105 painter->drawLine( snapSegmentPix1, curPointPix );
106 }
107 }
108
109 // Draw segment par/per input
110 if ( mAdvancedDigitizingDockWidget->betweenLineConstraint() != Qgis::BetweenLineConstraint::NoConstraint && hasSnappedSegment )
111 {
112 painter->setPen( mConstruction2Pen );
113 painter->drawLine( snapSegmentPix1, snapSegmentPix2 );
114 }
115
116 // Draw angle
117 if ( nPoints > 1 )
118 {
119 double a0, a;
120 if ( mAdvancedDigitizingDockWidget->constraintAngle()->relative() && nPoints > 2 )
121 {
122 a0 = std::atan2( -( prevPoint.y() - penulPoint.y() ), prevPoint.x() - penulPoint.x() );
123 }
124 else
125 {
126 a0 = 0;
127 }
128 if ( mAdvancedDigitizingDockWidget->constraintAngle()->isLocked() )
129 {
130 a = a0 - mAdvancedDigitizingDockWidget->constraintAngle()->value() * M_PI / 180;
131 }
132 else
133 {
134 a = std::atan2( -( curPoint.y() - prevPoint.y() ), curPoint.x() - prevPoint.x() );
135 }
136
137 a0 += canvasRotationRad;
138 a += canvasRotationRad;
139
140 painter->setPen( mConstruction2Pen );
141 painter->drawArc( QRectF( prevPointPix.x() - 20,
142 prevPointPix.y() - 20,
143 40, 40 ),
144 static_cast<int>( 16 * -a0 * 180 / M_PI ),
145 static_cast<int>( 16 * ( a0 - a ) * 180 / M_PI ) );
146 painter->drawLine( prevPointPix,
147 prevPointPix + 60 * QPointF( std::cos( a0 ), std::sin( a0 ) ) );
148
149
150 if ( mAdvancedDigitizingDockWidget->constraintAngle()->isLocked() )
151 {
152 painter->setPen( mLockedPen );
153 const double canvasPadding = QLineF( prevPointPix, curPointPix ).length();
154 painter->drawLine( prevPointPix + ( canvasPadding - canvasDiagonalDimension ) * QPointF( std::cos( a ), std::sin( a ) ),
155 prevPointPix + ( canvasPadding + canvasDiagonalDimension ) * QPointF( std::cos( a ), std::sin( a ) ) );
156 }
157 }
158
159 // Draw distance
160 if ( nPoints > 1 && mAdvancedDigitizingDockWidget->constraintDistance()->isLocked() )
161 {
162 painter->setPen( mLockedPen );
163 const double r = mAdvancedDigitizingDockWidget->constraintDistance()->value() / mupp;
164 QPainterPath ellipsePath;
165 ellipsePath.addEllipse( prevPointPix, r, r );
166 const double a = std::atan2( -( curPoint.y() - prevPoint.y() ), curPoint.x() - prevPoint.x() ) + canvasRotationRad;
167 const QTransform t = QTransform().translate( prevPointPix.x(), prevPointPix.y() ).rotateRadians( a ).translate( -prevPointPix.x(), -prevPointPix.y() );
168 const QPolygonF ellipsePoly = ellipsePath.toFillPolygon( t );
169 painter->drawPolygon( ellipsePoly );
170 }
171
172 // Draw x
173 if ( mAdvancedDigitizingDockWidget->constraintX()->isLocked() )
174 {
175 double x = 0.0;
176 bool draw = true;
177 painter->setPen( mLockedPen );
178 if ( mAdvancedDigitizingDockWidget->constraintX()->relative() )
179 {
180 if ( nPoints > 1 )
181 {
182 x = mAdvancedDigitizingDockWidget->constraintX()->value() + prevPoint.x();
183 }
184 else
185 {
186 draw = false;
187 }
188 }
189 else
190 {
191 x = mAdvancedDigitizingDockWidget->constraintX()->value();
192 }
193 if ( draw )
194 {
195 painter->drawLine( toCanvasCoordinates( QgsPointXY( x, mapPoly[0].y() ) ) - canvasDiagonalDimension * QPointF( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) ),
196 toCanvasCoordinates( QgsPointXY( x, mapPoly[0].y() ) ) + canvasDiagonalDimension * QPointF( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) ) );
197 }
198 }
199
200 // Draw y
201 if ( mAdvancedDigitizingDockWidget->constraintY()->isLocked() )
202 {
203 double y = 0.0;
204 bool draw = true;
205 painter->setPen( mLockedPen );
206 if ( mAdvancedDigitizingDockWidget->constraintY()->relative() )
207 {
208 if ( nPoints > 1 )
209 {
210 y = mAdvancedDigitizingDockWidget->constraintY()->value() + prevPoint.y();
211 }
212 else
213 {
214 draw = false;
215 }
216 }
217 else
218 {
219 y = mAdvancedDigitizingDockWidget->constraintY()->value();
220 }
221 if ( draw )
222 {
223 painter->drawLine( toCanvasCoordinates( QgsPointXY( mapPoly[0].x(), y ) ) - canvasDiagonalDimension * QPointF( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) ),
224 toCanvasCoordinates( QgsPointXY( mapPoly[0].x(), y ) ) + canvasDiagonalDimension * QPointF( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) ) );
225
226 }
227 }
228
229 // Draw constr
230 if ( mAdvancedDigitizingDockWidget->betweenLineConstraint() == Qgis::BetweenLineConstraint::NoConstraint )
231 {
232 if ( curPointExist && previousPointExist )
233 {
234 painter->setPen( mConstruction2Pen );
235 painter->drawLine( prevPointPix, curPointPix );
236 }
237
238 if ( previousPointExist && penulPointExist )
239 {
240 painter->setPen( mConstruction1Pen );
241 painter->drawLine( penulPointPix, prevPointPix );
242 }
243 }
244
245 if ( curPointExist )
246 {
247 painter->setPen( mCursorPen );
248 painter->drawLine( curPointPix + QPointF( -5, -5 ),
249 curPointPix + QPointF( +5, +5 ) );
250 painter->drawLine( curPointPix + QPointF( -5, +5 ),
251 curPointPix + QPointF( +5, -5 ) );
252 }
253
254 auto lineExtensionSide = mAdvancedDigitizingDockWidget->lineExtensionSide();
255 if ( mAdvancedDigitizingDockWidget->constraintLineExtension()->isLocked() &&
256 lineExtensionSide != Qgis::LineExtensionSide::NoVertex &&
257 !mAdvancedDigitizingDockWidget->lockedSnapVertices().isEmpty() )
258 {
259 painter->setPen( mLockedPen );
260
261 const QgsPointLocator::Match snap = mAdvancedDigitizingDockWidget->lockedSnapVertices().constLast();
262 const QPointF snappedPoint = toCanvasCoordinates( snap.point() );
263
264 const QgsFeature feature = snap.layer()->getFeature( snap.featureId() );
265 const QgsGeometry geom = feature.geometry();
266
267 QgsPoint vertex;
268 if ( lineExtensionSide == Qgis::LineExtensionSide::BeforeVertex )
269 {
270 vertex = geom.vertexAt( snap.vertexIndex() - 1 );
271 }
272 else
273 {
274 vertex = geom.vertexAt( snap.vertexIndex() + 1 );
275 }
276
277 if ( !vertex.isEmpty() )
278 {
279 const QPointF point = toCanvasCoordinates( vertex );
280 const double angle = std::atan2( snappedPoint.y() - point.y(), snappedPoint.x() - point.x() );
281
282 const double canvasPadding = QLineF( snappedPoint, curPointPix ).length();
283 painter->drawLine( snappedPoint + ( canvasPadding - canvasDiagonalDimension ) * QPointF( std::cos( angle ), std::sin( angle ) ),
284 snappedPoint + ( canvasPadding + canvasDiagonalDimension ) * QPointF( std::cos( angle ), std::sin( angle ) ) );
285 }
286
287 }
288
289 if ( mAdvancedDigitizingDockWidget->constraintXyVertex()->isLocked() )
290 {
291 painter->setPen( mLockedPen );
292
293 double coordinateExtension = mAdvancedDigitizingDockWidget->softLockX();
294 if ( coordinateExtension != std::numeric_limits<double>::quiet_NaN() )
295 {
296 const QgsPointXY point( coordinateExtension, mapPoly[0].y() );
297 const QPointF rotation( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) );
298 painter->drawLine( toCanvasCoordinates( point ) - canvasDiagonalDimension * rotation,
299 toCanvasCoordinates( point ) + canvasDiagonalDimension * rotation );
300 }
301
302 coordinateExtension = mAdvancedDigitizingDockWidget->softLockY();
303 if ( coordinateExtension != std::numeric_limits<double>::quiet_NaN() )
304 {
305 const QgsPointXY point( mapPoly[0].x(), coordinateExtension );
306 const QPointF rotation( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) );
307 painter->drawLine( toCanvasCoordinates( point ) - canvasDiagonalDimension * rotation,
308 toCanvasCoordinates( point ) + canvasDiagonalDimension * rotation );
309 }
310 }
311
312 painter->setPen( mCursorPen );
313
314 const QList< QgsPointLocator::Match > lockedSnapVertices = mAdvancedDigitizingDockWidget->lockedSnapVertices();
315 for ( const QgsPointLocator::Match &snapMatch : lockedSnapVertices )
316 {
317 const QgsPointXY point = snapMatch.point();
318 const QPointF canvasPoint = toCanvasCoordinates( point );
319
320 painter->drawLine( canvasPoint + QPointF( 5, 5 ),
321 canvasPoint - QPointF( 5, 5 ) );
322 painter->drawLine( canvasPoint + QPointF( -5, 5 ),
323 canvasPoint - QPointF( -5, 5 ) );
324 }
325
326 if ( !lockedSnapVertices.isEmpty() )
327 {
328 const QgsPointXY point = lockedSnapVertices.last().point();
329 const QPointF canvasPoint = toCanvasCoordinates( point );
330
331 painter->drawLine( canvasPoint + QPointF( 0, 5 ),
332 canvasPoint - QPointF( 0, 5 ) );
333 painter->drawLine( canvasPoint + QPointF( 5, 0 ),
334 canvasPoint - QPointF( 5, 0 ) );
335 }
336}
337
339{
340 // Use visible polygon rather than extent to properly handle rotated maps
341 QPolygonF mapPoly = mMapCanvas->mapSettings().visiblePolygon();
342 const double canvasWidth = QLineF( mapPoly[0], mapPoly[1] ).length();
343 const double canvasHeight = QLineF( mapPoly[0], mapPoly[3] ).length();
344 const QgsRectangle mapRect = QgsRectangle( mapPoly[0],
346 mapPoly[0].x() + canvasWidth,
347 mapPoly[0].y() - canvasHeight
348 )
349 );
350 if ( rect() != mapRect )
351 setRect( mapRect );
352}
@ 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:162
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:93
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:60
double y
Definition: qgspointxy.h:64
Q_GADGET double x
Definition: qgspointxy.h:63
QPointF toQPointF() const
Converts a point to a QPointF.
Definition: qgspointxy.h:166
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
bool isEmpty() const override
Returns true if the geometry is empty.
Definition: qgspoint.cpp:733
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:716
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)