QGIS API Documentation 3.28.14-Firenze (exported)
Loading...
Searching...
No Matches
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
6 email : denis.rouzaud@gmail.com
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 QPainterPath ellipsePath;
166 ellipsePath.addEllipse( prevPointPix, r, r );
167 const double a = std::atan2( -( curPoint.y() - prevPoint.y() ), curPoint.x() - prevPoint.x() ) + canvasRotationRad;
168 const QTransform t = QTransform().translate( prevPointPix.x(), prevPointPix.y() ).rotateRadians( a ).translate( -prevPointPix.x(), -prevPointPix.y() );
169 const QPolygonF ellipsePoly = ellipsePath.toFillPolygon( t );
170 painter->drawPolygon( ellipsePoly );
171 }
172
173 // Draw x
174 if ( mAdvancedDigitizingDockWidget->constraintX()->isLocked() )
175 {
176 double x = 0.0;
177 bool draw = true;
178 painter->setPen( mLockedPen );
179 if ( mAdvancedDigitizingDockWidget->constraintX()->relative() )
180 {
181 if ( nPoints > 1 )
182 {
183 x = mAdvancedDigitizingDockWidget->constraintX()->value() + prevPoint.x();
184 }
185 else
186 {
187 draw = false;
188 }
189 }
190 else
191 {
192 x = mAdvancedDigitizingDockWidget->constraintX()->value();
193 }
194 if ( draw )
195 {
196 painter->drawLine( toCanvasCoordinates( QgsPointXY( x, mapPoly[0].y() ) ) - canvasDiagonalDimension * QPointF( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) ),
197 toCanvasCoordinates( QgsPointXY( x, mapPoly[0].y() ) ) + canvasDiagonalDimension * QPointF( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) ) );
198 }
199 }
200
201 // Draw y
202 if ( mAdvancedDigitizingDockWidget->constraintY()->isLocked() )
203 {
204 double y = 0.0;
205 bool draw = true;
206 painter->setPen( mLockedPen );
207 if ( mAdvancedDigitizingDockWidget->constraintY()->relative() )
208 {
209 if ( nPoints > 1 )
210 {
211 y = mAdvancedDigitizingDockWidget->constraintY()->value() + prevPoint.y();
212 }
213 else
214 {
215 draw = false;
216 }
217 }
218 else
219 {
220 y = mAdvancedDigitizingDockWidget->constraintY()->value();
221 }
222 if ( draw )
223 {
224 painter->drawLine( toCanvasCoordinates( QgsPointXY( mapPoly[0].x(), y ) ) - canvasDiagonalDimension * QPointF( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) ),
225 toCanvasCoordinates( QgsPointXY( mapPoly[0].x(), y ) ) + canvasDiagonalDimension * QPointF( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) ) );
226
227 }
228 }
229
230 // Draw constr
231 if ( mAdvancedDigitizingDockWidget->betweenLineConstraint() == Qgis::BetweenLineConstraint::NoConstraint )
232 {
233 if ( curPointExist && previousPointExist )
234 {
235 painter->setPen( mConstruction2Pen );
236 painter->drawLine( prevPointPix, curPointPix );
237 }
238
239 if ( previousPointExist && penulPointExist )
240 {
241 painter->setPen( mConstruction1Pen );
242 painter->drawLine( penulPointPix, prevPointPix );
243 }
244 }
245
246 if ( curPointExist )
247 {
248 painter->setPen( mCursorPen );
249 painter->drawLine( curPointPix + QPointF( -5, -5 ),
250 curPointPix + QPointF( +5, +5 ) );
251 painter->drawLine( curPointPix + QPointF( -5, +5 ),
252 curPointPix + QPointF( +5, -5 ) );
253 }
254
255 auto lineExtensionSide = mAdvancedDigitizingDockWidget->lineExtensionSide();
256 if ( mAdvancedDigitizingDockWidget->constraintLineExtension()->isLocked() &&
257 lineExtensionSide != Qgis::LineExtensionSide::NoVertex &&
258 !mAdvancedDigitizingDockWidget->lockedSnapVertices().isEmpty() )
259 {
260 painter->setPen( mLockedPen );
261
262 const QgsPointLocator::Match snap = mAdvancedDigitizingDockWidget->lockedSnapVertices().last();
263 const QPointF snappedPoint = toCanvasCoordinates( snap.point() );
264
265 const QgsFeature feature = snap.layer()->getFeature( snap.featureId() );
266 const QgsGeometry geom = feature.geometry();
267
268 QgsPoint vertex;
269 if ( lineExtensionSide == Qgis::LineExtensionSide::BeforeVertex )
270 {
271 vertex = geom.vertexAt( snap.vertexIndex() - 1 );
272 }
273 else
274 {
275 vertex = geom.vertexAt( snap.vertexIndex() + 1 );
276 }
277
278 if ( !vertex.isEmpty() )
279 {
280 const QPointF point = toCanvasCoordinates( vertex );
281 const double angle = std::atan2( snappedPoint.y() - point.y(), snappedPoint.x() - point.x() );
282
283 const double canvasPadding = QLineF( snappedPoint, curPointPix ).length();
284 painter->drawLine( snappedPoint + ( canvasPadding - canvasDiagonalDimension ) * QPointF( std::cos( angle ), std::sin( angle ) ),
285 snappedPoint + ( canvasPadding + canvasDiagonalDimension ) * QPointF( std::cos( angle ), std::sin( angle ) ) );
286 }
287
288 }
289
290 if ( mAdvancedDigitizingDockWidget->constraintXyVertex()->isLocked() )
291 {
292 painter->setPen( mLockedPen );
293
294 double coordinateExtension = mAdvancedDigitizingDockWidget->softLockX();
295 if ( coordinateExtension != std::numeric_limits<double>::quiet_NaN() )
296 {
297 const QgsPointXY point( coordinateExtension, mapPoly[0].y() );
298 const QPointF rotation( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) );
299 painter->drawLine( toCanvasCoordinates( point ) - canvasDiagonalDimension * rotation,
300 toCanvasCoordinates( point ) + canvasDiagonalDimension * rotation );
301 }
302
303 coordinateExtension = mAdvancedDigitizingDockWidget->softLockY();
304 if ( coordinateExtension != std::numeric_limits<double>::quiet_NaN() )
305 {
306 const QgsPointXY point( mapPoly[0].x(), coordinateExtension );
307 const QPointF rotation( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) );
308 painter->drawLine( toCanvasCoordinates( point ) - canvasDiagonalDimension * rotation,
309 toCanvasCoordinates( point ) + canvasDiagonalDimension * rotation );
310 }
311 }
312
313 painter->setPen( mCursorPen );
314
315 const QList< QgsPointLocator::Match > lockedSnapVertices = mAdvancedDigitizingDockWidget->lockedSnapVertices();
316 for ( QgsPointLocator::Match snapMatch : lockedSnapVertices )
317 {
318 const QgsPointXY point = snapMatch.point();
319 const QPointF canvasPoint = toCanvasCoordinates( point );
320
321 painter->drawLine( canvasPoint + QPointF( 5, 5 ),
322 canvasPoint - QPointF( 5, 5 ) );
323 painter->drawLine( canvasPoint + QPointF( -5, 5 ),
324 canvasPoint - QPointF( -5, 5 ) );
325 }
326
327 if ( !lockedSnapVertices.isEmpty() )
328 {
329 const QgsPointXY point = lockedSnapVertices.last().point();
330 const QPointF canvasPoint = toCanvasCoordinates( point );
331
332 painter->drawLine( canvasPoint + QPointF( 0, 5 ),
333 canvasPoint - QPointF( 0, 5 ) );
334 painter->drawLine( canvasPoint + QPointF( 5, 0 ),
335 canvasPoint - QPointF( 5, 0 ) );
336 }
337}
338
340{
341 // Use visible polygon rather than extent to properly handle rotated maps
342 QPolygonF mapPoly = mMapCanvas->mapSettings().visiblePolygon();
343 const double canvasWidth = QLineF( mapPoly[0], mapPoly[1] ).length();
344 const double canvasHeight = QLineF( mapPoly[0], mapPoly[3] ).length();
345 const QgsRectangle mapRect = QgsRectangle( mapPoly[0],
347 mapPoly[0].x() + canvasWidth,
348 mapPoly[0].y() - canvasHeight
349 )
350 );
351 if ( rect() != mapRect )
352 setRect( mapRect );
353}
@ 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.
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.
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.
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
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
Returns true if the geometry is empty.
Definition qgspoint.cpp:767
A rectangle specified with double values.
QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
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)