QGIS API Documentation 3.38.0-Grenoble (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
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
265 req.setFilterFid( snap.featureId() );
266 req.setNoAttributes();
268 QgsFeatureIterator featureIt = snap.layer()->getFeatures( req );
269
270 QgsFeature feature;
271 featureIt.nextFeature( feature );
272
273 const QgsGeometry geometry = feature.geometry();
274 const QgsAbstractGeometry *geom = geometry.constGet();
275
276 QgsPoint vertex;
277 QgsVertexId vertexId;
278 geometry.vertexIdFromVertexNr( snap.vertexIndex(), vertexId );
279 if ( vertexId.isValid() )
280 {
281 QgsVertexId previousVertexId;
282 QgsVertexId nextVertexId;
283 geom->adjacentVertices( vertexId, previousVertexId, nextVertexId );
284
285 if ( lineExtensionSide == Qgis::LineExtensionSide::BeforeVertex )
286 {
287 vertex = geom->vertexAt( previousVertexId );
288 }
289 else
290 {
291 vertex = geom->vertexAt( nextVertexId );
292 }
293 }
294
295 if ( !vertex.isEmpty() )
296 {
297 const QPointF point = toCanvasCoordinates( vertex );
298 const double angle = std::atan2( snappedPoint.y() - point.y(), snappedPoint.x() - point.x() );
299
300 const double canvasPadding = QLineF( snappedPoint, curPointPix ).length();
301 painter->drawLine( snappedPoint + ( canvasPadding - canvasDiagonalDimension ) * QPointF( std::cos( angle ), std::sin( angle ) ),
302 snappedPoint + ( canvasPadding + canvasDiagonalDimension ) * QPointF( std::cos( angle ), std::sin( angle ) ) );
303 }
304
305 }
306
307 if ( mAdvancedDigitizingDockWidget->constraintXyVertex()->isLocked() )
308 {
309 painter->setPen( mLockedPen );
310
311 double coordinateExtension = mAdvancedDigitizingDockWidget->softLockX();
312 if ( coordinateExtension != std::numeric_limits<double>::quiet_NaN() )
313 {
314 const QgsPointXY point( coordinateExtension, mapPoly[0].y() );
315 const QPointF rotation( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) );
316 painter->drawLine( toCanvasCoordinates( point ) - canvasDiagonalDimension * rotation,
317 toCanvasCoordinates( point ) + canvasDiagonalDimension * rotation );
318 }
319
320 coordinateExtension = mAdvancedDigitizingDockWidget->softLockY();
321 if ( coordinateExtension != std::numeric_limits<double>::quiet_NaN() )
322 {
323 const QgsPointXY point( mapPoly[0].x(), coordinateExtension );
324 const QPointF rotation( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) );
325 painter->drawLine( toCanvasCoordinates( point ) - canvasDiagonalDimension * rotation,
326 toCanvasCoordinates( point ) + canvasDiagonalDimension * rotation );
327 }
328 }
329
330 painter->setPen( mCursorPen );
331
332 const QList< QgsPointLocator::Match > lockedSnapVertices = mAdvancedDigitizingDockWidget->lockedSnapVertices();
333 for ( const QgsPointLocator::Match &snapMatch : lockedSnapVertices )
334 {
335 const QgsPointXY point = snapMatch.point();
336 const QPointF canvasPoint = toCanvasCoordinates( point );
337
338 painter->drawLine( canvasPoint + QPointF( 5, 5 ),
339 canvasPoint - QPointF( 5, 5 ) );
340 painter->drawLine( canvasPoint + QPointF( -5, 5 ),
341 canvasPoint - QPointF( -5, 5 ) );
342 }
343
344 if ( !lockedSnapVertices.isEmpty() )
345 {
346 const QgsPointXY point = lockedSnapVertices.last().point();
347 const QPointF canvasPoint = toCanvasCoordinates( point );
348
349 painter->drawLine( canvasPoint + QPointF( 0, 5 ),
350 canvasPoint - QPointF( 0, 5 ) );
351 painter->drawLine( canvasPoint + QPointF( 5, 0 ),
352 canvasPoint - QPointF( 5, 0 ) );
353 }
354}
355
357{
358 // Use visible polygon rather than extent to properly handle rotated maps
359 QPolygonF mapPoly = mMapCanvas->mapSettings().visiblePolygon();
360 const double canvasWidth = QLineF( mapPoly[0], mapPoly[1] ).length();
361 const double canvasHeight = QLineF( mapPoly[0], mapPoly[3] ).length();
362 const QgsRectangle mapRect = QgsRectangle( mapPoly[0],
364 mapPoly[0].x() + canvasWidth,
365 mapPoly[0].y() - canvasHeight
366 )
367 );
368 if ( rect() != mapRect )
369 setRect( mapRect );
370}
@ NoConstraint
No additional constraint.
@ NoVertex
Don't lock to vertex.
@ BeforeVertex
Lock to previous vertex.
Abstract base class for all geometries.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
virtual void adjacentVertices(QgsVertexId vertex, QgsVertexId &previousVertex, QgsVertexId &nextVertex) const =0
Returns the vertices adjacent to a specified vertex within a geometry.
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.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature's geometries.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsGeometry geometry
Definition qgsfeature.h:69
A geometry is the spatial representation of a feature.
bool vertexIdFromVertexNr(int number, QgsVertexId &id) const
Calculates the vertex ID from a vertex number.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
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)
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
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
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:737
A rectangle specified with double values.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
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)
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:30
bool isValid() const
Returns true if the vertex id is valid.
Definition qgsvertexid.h:45