QGIS API Documentation 3.39.0-Master (47f7b3a4989)
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 , mConstructionGuidesPen( QPen( QColor( 20, 210, 150 ), 1, Qt::DashLine ) )
32 , mAdvancedDigitizingDockWidget( cadDockWidget )
33{
34}
35
37{
38 if ( !mAdvancedDigitizingDockWidget->cadEnabled() )
39 return;
40
41 painter->setRenderHint( QPainter::Antialiasing );
42 painter->setCompositionMode( QPainter::CompositionMode_Difference );
43
44 // Draw construction guides
45 if ( mAdvancedDigitizingDockWidget->showConstructionGuides() )
46 {
47 if ( QgsVectorLayer *constructionGuidesLayer = mAdvancedDigitizingDockWidget->constructionGuidesLayer() )
48 {
49 QgsFeatureIterator it = constructionGuidesLayer->getFeatures( QgsFeatureRequest().setNoAttributes().setFilterRect( mMapCanvas->mapSettings().visibleExtent() ) );
50 QgsFeature feature;
51 painter->setPen( mConstructionGuidesPen );
52 while ( it.nextFeature( feature ) )
53 {
54 QgsGeometry geom = feature.geometry();
56 const QPolygonF polygon = geom.asQPolygonF();
57 painter->drawPolyline( polygon );
58 }
59 }
60 }
61
62 // Use visible polygon rather than extent to properly handle rotated maps
63 QPolygonF mapPoly = mMapCanvas->mapSettings().visiblePolygon();
64 const double canvasWidth = QLineF( mapPoly[0], mapPoly[1] ).length();
65 const double canvasHeight = QLineF( mapPoly[0], mapPoly[3] ).length();
66
67 const int nPoints = mAdvancedDigitizingDockWidget->pointsCount();
68 if ( !nPoints )
69 return;
70
71 bool previousPointExist, penulPointExist;
72 const QgsPointXY curPoint = mAdvancedDigitizingDockWidget->currentPointV2();
73 const QgsPointXY prevPoint = mAdvancedDigitizingDockWidget->previousPointV2( &previousPointExist );
74 const QgsPointXY penulPoint = mAdvancedDigitizingDockWidget->penultimatePointV2( &penulPointExist );
75 const bool snappedToVertex = mAdvancedDigitizingDockWidget->snappedToVertex();
76 const QList<QgsPointXY> snappedSegment = mAdvancedDigitizingDockWidget->snappedSegment();
77 const bool hasSnappedSegment = snappedSegment.count() == 2;
78
79 const bool curPointExist = mapPoly.containsPoint( curPoint.toQPointF(), Qt::OddEvenFill );
80
81 const double mupp = mMapCanvas->getCoordinateTransform()->mapUnitsPerPixel();
82 if ( mupp == 0 )
83 return;
84
85 const double canvasRotationRad = mMapCanvas->rotation() * M_PI / 180;
86 const double canvasDiagonalDimension = ( canvasWidth + canvasHeight ) / mupp ;
87
88 QPointF curPointPix, prevPointPix, penulPointPix, snapSegmentPix1, snapSegmentPix2;
89
90 if ( curPointExist )
91 {
92 curPointPix = toCanvasCoordinates( curPoint );
93 }
94 if ( previousPointExist )
95 {
96 prevPointPix = toCanvasCoordinates( prevPoint );
97 }
98 if ( penulPointExist )
99 {
100 penulPointPix = toCanvasCoordinates( penulPoint );
101 }
102 if ( hasSnappedSegment )
103 {
104 snapSegmentPix1 = toCanvasCoordinates( snappedSegment[0] );
105 snapSegmentPix2 = toCanvasCoordinates( snappedSegment[1] );
106 }
107
108 // Draw point snap
109 if ( curPointExist && snappedToVertex )
110 {
111 painter->setPen( mSnapPen );
112 painter->drawEllipse( curPointPix, 10, 10 );
113 }
114
115 // Draw segment snap
116 if ( hasSnappedSegment && !snappedToVertex )
117 {
118 painter->setPen( mSnapPen );
119 painter->drawLine( snapSegmentPix1, snapSegmentPix2 );
120
121 if ( curPointExist )
122 {
123 painter->setPen( mSnapLinePen );
124 painter->drawLine( snapSegmentPix1, curPointPix );
125 }
126 }
127
128 // Draw segment par/per input
129 if ( mAdvancedDigitizingDockWidget->betweenLineConstraint() != Qgis::BetweenLineConstraint::NoConstraint && hasSnappedSegment )
130 {
131 painter->setPen( mConstruction2Pen );
132 painter->drawLine( snapSegmentPix1, snapSegmentPix2 );
133 }
134
135 // Draw angle
136 if ( nPoints > 1 )
137 {
138 double a0, a;
139 if ( mAdvancedDigitizingDockWidget->constraintAngle()->relative() && nPoints > 2 )
140 {
141 a0 = std::atan2( -( prevPoint.y() - penulPoint.y() ), prevPoint.x() - penulPoint.x() );
142 }
143 else
144 {
145 a0 = 0;
146 }
147 if ( mAdvancedDigitizingDockWidget->constraintAngle()->isLocked() )
148 {
149 a = a0 - mAdvancedDigitizingDockWidget->constraintAngle()->value() * M_PI / 180;
150 }
151 else
152 {
153 a = std::atan2( -( curPoint.y() - prevPoint.y() ), curPoint.x() - prevPoint.x() );
154 }
155
156 a0 += canvasRotationRad;
157 a += canvasRotationRad;
158
159 painter->setPen( mConstruction2Pen );
160 painter->drawArc( QRectF( prevPointPix.x() - 20,
161 prevPointPix.y() - 20,
162 40, 40 ),
163 static_cast<int>( 16 * -a0 * 180 / M_PI ),
164 static_cast<int>( 16 * ( a0 - a ) * 180 / M_PI ) );
165 painter->drawLine( prevPointPix,
166 prevPointPix + 60 * QPointF( std::cos( a0 ), std::sin( a0 ) ) );
167
168
169 if ( mAdvancedDigitizingDockWidget->constraintAngle()->isLocked() )
170 {
171 painter->setPen( mLockedPen );
172 const double canvasPadding = QLineF( prevPointPix, curPointPix ).length();
173 painter->drawLine( prevPointPix + ( canvasPadding - canvasDiagonalDimension ) * QPointF( std::cos( a ), std::sin( a ) ),
174 prevPointPix + ( canvasPadding + canvasDiagonalDimension ) * QPointF( std::cos( a ), std::sin( a ) ) );
175 }
176 }
177
178 // Draw distance
179 if ( nPoints > 1 && mAdvancedDigitizingDockWidget->constraintDistance()->isLocked() )
180 {
181 painter->setPen( mLockedPen );
182 const double r = mAdvancedDigitizingDockWidget->constraintDistance()->value() / mupp;
183 QPainterPath ellipsePath;
184 ellipsePath.addEllipse( prevPointPix, r, r );
185 const double a = std::atan2( -( curPoint.y() - prevPoint.y() ), curPoint.x() - prevPoint.x() ) + canvasRotationRad;
186 const QTransform t = QTransform().translate( prevPointPix.x(), prevPointPix.y() ).rotateRadians( a ).translate( -prevPointPix.x(), -prevPointPix.y() );
187 const QPolygonF ellipsePoly = ellipsePath.toFillPolygon( t );
188 painter->drawPolygon( ellipsePoly );
189 }
190
191 // Draw x
192 if ( mAdvancedDigitizingDockWidget->constraintX()->isLocked() )
193 {
194 double x = 0.0;
195 bool draw = true;
196 painter->setPen( mLockedPen );
197 if ( mAdvancedDigitizingDockWidget->constraintX()->relative() )
198 {
199 if ( nPoints > 1 )
200 {
201 x = mAdvancedDigitizingDockWidget->constraintX()->value() + prevPoint.x();
202 }
203 else
204 {
205 draw = false;
206 }
207 }
208 else
209 {
210 x = mAdvancedDigitizingDockWidget->constraintX()->value();
211 }
212 if ( draw )
213 {
214 painter->drawLine( toCanvasCoordinates( QgsPointXY( x, mapPoly[0].y() ) ) - canvasDiagonalDimension * QPointF( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) ),
215 toCanvasCoordinates( QgsPointXY( x, mapPoly[0].y() ) ) + canvasDiagonalDimension * QPointF( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) ) );
216 }
217 }
218
219 // Draw y
220 if ( mAdvancedDigitizingDockWidget->constraintY()->isLocked() )
221 {
222 double y = 0.0;
223 bool draw = true;
224 painter->setPen( mLockedPen );
225 if ( mAdvancedDigitizingDockWidget->constraintY()->relative() )
226 {
227 if ( nPoints > 1 )
228 {
229 y = mAdvancedDigitizingDockWidget->constraintY()->value() + prevPoint.y();
230 }
231 else
232 {
233 draw = false;
234 }
235 }
236 else
237 {
238 y = mAdvancedDigitizingDockWidget->constraintY()->value();
239 }
240 if ( draw )
241 {
242 painter->drawLine( toCanvasCoordinates( QgsPointXY( mapPoly[0].x(), y ) ) - canvasDiagonalDimension * QPointF( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) ),
243 toCanvasCoordinates( QgsPointXY( mapPoly[0].x(), y ) ) + canvasDiagonalDimension * QPointF( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) ) );
244
245 }
246 }
247
248 // Draw constraints
249 if ( mAdvancedDigitizingDockWidget->betweenLineConstraint() == Qgis::BetweenLineConstraint::NoConstraint )
250 {
251 if ( curPointExist && previousPointExist )
252 {
253 painter->setPen( mConstruction2Pen );
254 painter->drawLine( prevPointPix, curPointPix );
255 }
256
257 if ( previousPointExist && penulPointExist )
258 {
259 painter->setPen( mConstruction1Pen );
260 painter->drawLine( penulPointPix, prevPointPix );
261 }
262 }
263
264 if ( curPointExist )
265 {
266 painter->setPen( mCursorPen );
267 painter->drawLine( curPointPix + QPointF( -5, -5 ),
268 curPointPix + QPointF( +5, +5 ) );
269 painter->drawLine( curPointPix + QPointF( -5, +5 ),
270 curPointPix + QPointF( +5, -5 ) );
271 }
272
273 auto lineExtensionSide = mAdvancedDigitizingDockWidget->lineExtensionSide();
274 if ( mAdvancedDigitizingDockWidget->constraintLineExtension()->isLocked() &&
275 lineExtensionSide != Qgis::LineExtensionSide::NoVertex &&
276 !mAdvancedDigitizingDockWidget->lockedSnapVertices().isEmpty() )
277 {
278 painter->setPen( mLockedPen );
279
280 const QgsPointLocator::Match snap = mAdvancedDigitizingDockWidget->lockedSnapVertices().constLast();
281 const QPointF snappedPoint = toCanvasCoordinates( snap.point() );
282
284 req.setFilterFid( snap.featureId() );
285 req.setNoAttributes();
287 QgsFeatureIterator featureIt = snap.layer()->getFeatures( req );
288
289 QgsFeature feature;
290 featureIt.nextFeature( feature );
291
292 const QgsGeometry geometry = feature.geometry();
293 const QgsAbstractGeometry *geom = geometry.constGet();
294
295 QgsPoint vertex;
296 QgsVertexId vertexId;
297 geometry.vertexIdFromVertexNr( snap.vertexIndex(), vertexId );
298 if ( vertexId.isValid() )
299 {
300 QgsVertexId previousVertexId;
301 QgsVertexId nextVertexId;
302 geom->adjacentVertices( vertexId, previousVertexId, nextVertexId );
303
304 if ( lineExtensionSide == Qgis::LineExtensionSide::BeforeVertex )
305 {
306 vertex = geom->vertexAt( previousVertexId );
307 }
308 else
309 {
310 vertex = geom->vertexAt( nextVertexId );
311 }
312 }
313
314 if ( !vertex.isEmpty() )
315 {
316 const QPointF point = toCanvasCoordinates( vertex );
317 const double angle = std::atan2( snappedPoint.y() - point.y(), snappedPoint.x() - point.x() );
318
319 const double canvasPadding = QLineF( snappedPoint, curPointPix ).length();
320 painter->drawLine( snappedPoint + ( canvasPadding - canvasDiagonalDimension ) * QPointF( std::cos( angle ), std::sin( angle ) ),
321 snappedPoint + ( canvasPadding + canvasDiagonalDimension ) * QPointF( std::cos( angle ), std::sin( angle ) ) );
322 }
323
324 }
325
326 if ( mAdvancedDigitizingDockWidget->constraintXyVertex()->isLocked() )
327 {
328 painter->setPen( mLockedPen );
329
330 double coordinateExtension = mAdvancedDigitizingDockWidget->softLockX();
331 if ( coordinateExtension != std::numeric_limits<double>::quiet_NaN() )
332 {
333 const QgsPointXY point( coordinateExtension, mapPoly[0].y() );
334 const QPointF rotation( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) );
335 painter->drawLine( toCanvasCoordinates( point ) - canvasDiagonalDimension * rotation,
336 toCanvasCoordinates( point ) + canvasDiagonalDimension * rotation );
337 }
338
339 coordinateExtension = mAdvancedDigitizingDockWidget->softLockY();
340 if ( coordinateExtension != std::numeric_limits<double>::quiet_NaN() )
341 {
342 const QgsPointXY point( mapPoly[0].x(), coordinateExtension );
343 const QPointF rotation( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) );
344 painter->drawLine( toCanvasCoordinates( point ) - canvasDiagonalDimension * rotation,
345 toCanvasCoordinates( point ) + canvasDiagonalDimension * rotation );
346 }
347 }
348
349 painter->setPen( mCursorPen );
350
351 const QList< QgsPointLocator::Match > lockedSnapVertices = mAdvancedDigitizingDockWidget->lockedSnapVertices();
352 for ( const QgsPointLocator::Match &snapMatch : lockedSnapVertices )
353 {
354 const QgsPointXY point = snapMatch.point();
355 const QPointF canvasPoint = toCanvasCoordinates( point );
356
357 painter->drawLine( canvasPoint + QPointF( 5, 5 ),
358 canvasPoint - QPointF( 5, 5 ) );
359 painter->drawLine( canvasPoint + QPointF( -5, 5 ),
360 canvasPoint - QPointF( -5, 5 ) );
361 }
362
363 if ( !lockedSnapVertices.isEmpty() )
364 {
365 const QgsPointXY point = lockedSnapVertices.last().point();
366 const QPointF canvasPoint = toCanvasCoordinates( point );
367
368 painter->drawLine( canvasPoint + QPointF( 0, 5 ),
369 canvasPoint - QPointF( 0, 5 ) );
370 painter->drawLine( canvasPoint + QPointF( 5, 0 ),
371 canvasPoint - QPointF( 5, 0 ) );
372 }
373}
374
376{
377 // Use visible polygon rather than extent to properly handle rotated maps
378 QPolygonF mapPoly = mMapCanvas->mapSettings().visiblePolygon();
379 const double canvasWidth = QLineF( mapPoly[0], mapPoly[1] ).length();
380 const double canvasHeight = QLineF( mapPoly[0], mapPoly[3] ).length();
381 const QgsRectangle mapRect = QgsRectangle( mapPoly[0],
383 mapPoly[0].x() + canvasWidth,
384 mapPoly[0].y() - canvasHeight
385 )
386 );
387 if ( rect() != mapRect )
388 setRect( mapRect );
389}
@ 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.
bool showConstructionGuides() const
Returns whether the construction guides are visible.
const CadConstraint * constraintXyVertex() const
Returns the CadConstraint.
const CadConstraint * constraintY() const
Returns the CadConstraint on the Y coordinate.
QgsVectorLayer * constructionGuidesLayer() const
Returns the vector layer within which construction guides are stored.
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.
QPolygonF asQPolygonF() const
Returns contents of the geometry as a QPolygonF.
bool vertexIdFromVertexNr(int number, QgsVertexId &id) const
Calculates the vertex ID from a vertex number.
void mapToPixel(const QgsMapToPixel &mtp)
Transforms the geometry from map units to pixels in place.
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)
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes output image size into account.
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.
Represents a vector layer which manages a vector based data sets.
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