QGIS API Documentation 3.41.0-Master (cea29feecf2)
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 // Draw current tool
63 if ( QgsAdvancedDigitizingTool *tool = mAdvancedDigitizingDockWidget->tool() )
64 {
65 // if a tool is active in the dock, then delegate to that tool to handle decorating the canvas instead of using the default decorations
66 tool->paint( painter );
67 return;
68 }
69
70 // Use visible polygon rather than extent to properly handle rotated maps
71 QPolygonF mapPoly = mMapCanvas->mapSettings().visiblePolygon();
72 const double canvasWidth = QLineF( mapPoly[0], mapPoly[1] ).length();
73 const double canvasHeight = QLineF( mapPoly[0], mapPoly[3] ).length();
74
75 const int nPoints = mAdvancedDigitizingDockWidget->pointsCount();
76 if ( !nPoints )
77 return;
78
79 bool previousPointExist, penulPointExist;
80 const QgsPointXY curPoint = mAdvancedDigitizingDockWidget->currentPointV2();
81 const QgsPointXY prevPoint = mAdvancedDigitizingDockWidget->previousPointV2( &previousPointExist );
82 const QgsPointXY penulPoint = mAdvancedDigitizingDockWidget->penultimatePointV2( &penulPointExist );
83 const bool snappedToVertex = mAdvancedDigitizingDockWidget->snappedToVertex();
84 const QList<QgsPointXY> snappedSegment = mAdvancedDigitizingDockWidget->snappedSegment();
85 const bool hasSnappedSegment = snappedSegment.count() == 2;
86
87 const bool curPointExist = mapPoly.containsPoint( curPoint.toQPointF(), Qt::OddEvenFill );
88
89 const double mupp = mMapCanvas->getCoordinateTransform()->mapUnitsPerPixel();
90 if ( mupp == 0 )
91 return;
92
93 const double canvasRotationRad = mMapCanvas->rotation() * M_PI / 180;
94 const double canvasDiagonalDimension = ( canvasWidth + canvasHeight ) / mupp;
95
96 QPointF curPointPix, prevPointPix, penulPointPix, snapSegmentPix1, snapSegmentPix2;
97
98 if ( curPointExist )
99 {
100 curPointPix = toCanvasCoordinates( curPoint );
101 }
102 if ( previousPointExist )
103 {
104 prevPointPix = toCanvasCoordinates( prevPoint );
105 }
106 if ( penulPointExist )
107 {
108 penulPointPix = toCanvasCoordinates( penulPoint );
109 }
110 if ( hasSnappedSegment )
111 {
112 snapSegmentPix1 = toCanvasCoordinates( snappedSegment[0] );
113 snapSegmentPix2 = toCanvasCoordinates( snappedSegment[1] );
114 }
115
116 // Draw point snap
117 if ( curPointExist && snappedToVertex )
118 {
119 painter->setPen( mSnapPen );
120 painter->drawEllipse( curPointPix, 10, 10 );
121 }
122
123 // Draw segment snap
124 if ( hasSnappedSegment && !snappedToVertex )
125 {
126 painter->setPen( mSnapPen );
127 painter->drawLine( snapSegmentPix1, snapSegmentPix2 );
128
129 if ( curPointExist )
130 {
131 painter->setPen( mSnapLinePen );
132 painter->drawLine( snapSegmentPix1, curPointPix );
133 }
134 }
135
136 // Draw segment par/per input
137 if ( mAdvancedDigitizingDockWidget->betweenLineConstraint() != Qgis::BetweenLineConstraint::NoConstraint && hasSnappedSegment )
138 {
139 painter->setPen( mConstruction2Pen );
140 painter->drawLine( snapSegmentPix1, snapSegmentPix2 );
141 }
142
143 // Draw angle
144 if ( nPoints > 1 )
145 {
146 double a0, a;
147 if ( mAdvancedDigitizingDockWidget->constraintAngle()->relative() && nPoints > 2 )
148 {
149 a0 = std::atan2( -( prevPoint.y() - penulPoint.y() ), prevPoint.x() - penulPoint.x() );
150 }
151 else
152 {
153 a0 = 0;
154 }
155 if ( mAdvancedDigitizingDockWidget->constraintAngle()->isLocked() )
156 {
157 a = a0 - mAdvancedDigitizingDockWidget->constraintAngle()->value() * M_PI / 180;
158 }
159 else
160 {
161 a = std::atan2( -( curPoint.y() - prevPoint.y() ), curPoint.x() - prevPoint.x() );
162 }
163
164 a0 += canvasRotationRad;
165 a += canvasRotationRad;
166
167 painter->setPen( mConstruction2Pen );
168 painter->drawArc( QRectF( prevPointPix.x() - 20, prevPointPix.y() - 20, 40, 40 ), static_cast<int>( 16 * -a0 * 180 / M_PI ), static_cast<int>( 16 * ( a0 - a ) * 180 / M_PI ) );
169 painter->drawLine( prevPointPix, prevPointPix + 60 * QPointF( std::cos( a0 ), std::sin( a0 ) ) );
170
171
172 if ( mAdvancedDigitizingDockWidget->constraintAngle()->isLocked() )
173 {
174 painter->setPen( mLockedPen );
175 const double canvasPadding = QLineF( prevPointPix, curPointPix ).length();
176 painter->drawLine( prevPointPix + ( canvasPadding - canvasDiagonalDimension ) * QPointF( std::cos( a ), std::sin( a ) ), prevPointPix + ( canvasPadding + canvasDiagonalDimension ) * QPointF( std::cos( a ), std::sin( a ) ) );
177 }
178 }
179
180 // Draw distance
181 if ( nPoints > 1 && mAdvancedDigitizingDockWidget->constraintDistance()->isLocked() )
182 {
183 painter->setPen( mLockedPen );
184 const double r = mAdvancedDigitizingDockWidget->constraintDistance()->value() / mupp;
185 QPainterPath ellipsePath;
186 ellipsePath.addEllipse( prevPointPix, r, r );
187 const double a = std::atan2( -( curPoint.y() - prevPoint.y() ), curPoint.x() - prevPoint.x() ) + canvasRotationRad;
188 const QTransform t = QTransform().translate( prevPointPix.x(), prevPointPix.y() ).rotateRadians( a ).translate( -prevPointPix.x(), -prevPointPix.y() );
189 const QPolygonF ellipsePoly = ellipsePath.toFillPolygon( t );
190 painter->drawPolygon( ellipsePoly );
191 }
192
193 // Draw x
194 if ( mAdvancedDigitizingDockWidget->constraintX()->isLocked() )
195 {
196 double x = 0.0;
197 bool draw = true;
198 painter->setPen( mLockedPen );
199 if ( mAdvancedDigitizingDockWidget->constraintX()->relative() )
200 {
201 if ( nPoints > 1 )
202 {
203 x = mAdvancedDigitizingDockWidget->constraintX()->value() + prevPoint.x();
204 }
205 else
206 {
207 draw = false;
208 }
209 }
210 else
211 {
212 x = mAdvancedDigitizingDockWidget->constraintX()->value();
213 }
214 if ( draw )
215 {
216 painter->drawLine( toCanvasCoordinates( QgsPointXY( x, mapPoly[0].y() ) ) - canvasDiagonalDimension * QPointF( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) ), toCanvasCoordinates( QgsPointXY( x, mapPoly[0].y() ) ) + canvasDiagonalDimension * QPointF( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) ) );
217 }
218 }
219
220 // Draw y
221 if ( mAdvancedDigitizingDockWidget->constraintY()->isLocked() )
222 {
223 double y = 0.0;
224 bool draw = true;
225 painter->setPen( mLockedPen );
226 if ( mAdvancedDigitizingDockWidget->constraintY()->relative() )
227 {
228 if ( nPoints > 1 )
229 {
230 y = mAdvancedDigitizingDockWidget->constraintY()->value() + prevPoint.y();
231 }
232 else
233 {
234 draw = false;
235 }
236 }
237 else
238 {
239 y = mAdvancedDigitizingDockWidget->constraintY()->value();
240 }
241 if ( draw )
242 {
243 painter->drawLine( toCanvasCoordinates( QgsPointXY( mapPoly[0].x(), y ) ) - canvasDiagonalDimension * QPointF( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) ), toCanvasCoordinates( QgsPointXY( mapPoly[0].x(), y ) ) + canvasDiagonalDimension * QPointF( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) ) );
244 }
245 }
246
247 // Draw constraints
248 if ( mAdvancedDigitizingDockWidget->betweenLineConstraint() == Qgis::BetweenLineConstraint::NoConstraint )
249 {
250 if ( curPointExist && previousPointExist )
251 {
252 painter->setPen( mConstruction2Pen );
253 painter->drawLine( prevPointPix, curPointPix );
254 }
255
256 if ( previousPointExist && penulPointExist )
257 {
258 painter->setPen( mConstruction1Pen );
259 painter->drawLine( penulPointPix, prevPointPix );
260 }
261 }
262
263 if ( curPointExist )
264 {
265 painter->setPen( mCursorPen );
266 painter->drawLine( curPointPix + QPointF( -5, -5 ), curPointPix + QPointF( +5, +5 ) );
267 painter->drawLine( curPointPix + QPointF( -5, +5 ), curPointPix + QPointF( +5, -5 ) );
268 }
269
270 auto lineExtensionSide = mAdvancedDigitizingDockWidget->lineExtensionSide();
271 if ( mAdvancedDigitizingDockWidget->constraintLineExtension()->isLocked() && lineExtensionSide != Qgis::LineExtensionSide::NoVertex && !mAdvancedDigitizingDockWidget->lockedSnapVertices().isEmpty() )
272 {
273 painter->setPen( mLockedPen );
274
275 const QgsPointLocator::Match snap = mAdvancedDigitizingDockWidget->lockedSnapVertices().constLast();
276 const QPointF snappedPoint = toCanvasCoordinates( snap.point() );
277
279 req.setFilterFid( snap.featureId() );
280 req.setNoAttributes();
282 QgsFeatureIterator featureIt = snap.layer()->getFeatures( req );
283
284 QgsFeature feature;
285 featureIt.nextFeature( feature );
286
287 const QgsGeometry geometry = feature.geometry();
288 const QgsAbstractGeometry *geom = geometry.constGet();
289
290 QgsPoint vertex;
291 QgsVertexId vertexId;
292 geometry.vertexIdFromVertexNr( snap.vertexIndex(), vertexId );
293 if ( vertexId.isValid() )
294 {
295 QgsVertexId previousVertexId;
296 QgsVertexId nextVertexId;
297 geom->adjacentVertices( vertexId, previousVertexId, nextVertexId );
298
299 if ( lineExtensionSide == Qgis::LineExtensionSide::BeforeVertex )
300 {
301 vertex = geom->vertexAt( previousVertexId );
302 }
303 else
304 {
305 vertex = geom->vertexAt( nextVertexId );
306 }
307 }
308
309 if ( !vertex.isEmpty() )
310 {
311 const QPointF point = toCanvasCoordinates( vertex );
312 const double angle = std::atan2( snappedPoint.y() - point.y(), snappedPoint.x() - point.x() );
313
314 const double canvasPadding = QLineF( snappedPoint, curPointPix ).length();
315 painter->drawLine( snappedPoint + ( canvasPadding - canvasDiagonalDimension ) * QPointF( std::cos( angle ), std::sin( angle ) ), snappedPoint + ( canvasPadding + canvasDiagonalDimension ) * QPointF( std::cos( angle ), std::sin( angle ) ) );
316 }
317 }
318
319 if ( mAdvancedDigitizingDockWidget->constraintXyVertex()->isLocked() )
320 {
321 painter->setPen( mLockedPen );
322
323 double coordinateExtension = mAdvancedDigitizingDockWidget->softLockX();
324 if ( coordinateExtension != std::numeric_limits<double>::quiet_NaN() )
325 {
326 const QgsPointXY point( coordinateExtension, mapPoly[0].y() );
327 const QPointF rotation( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) );
328 painter->drawLine( toCanvasCoordinates( point ) - canvasDiagonalDimension * rotation, toCanvasCoordinates( point ) + canvasDiagonalDimension * rotation );
329 }
330
331 coordinateExtension = mAdvancedDigitizingDockWidget->softLockY();
332 if ( coordinateExtension != std::numeric_limits<double>::quiet_NaN() )
333 {
334 const QgsPointXY point( mapPoly[0].x(), coordinateExtension );
335 const QPointF rotation( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) );
336 painter->drawLine( toCanvasCoordinates( point ) - canvasDiagonalDimension * rotation, toCanvasCoordinates( point ) + canvasDiagonalDimension * rotation );
337 }
338 }
339
340 painter->setPen( mCursorPen );
341
342 const QList<QgsPointLocator::Match> lockedSnapVertices = mAdvancedDigitizingDockWidget->lockedSnapVertices();
343 for ( const QgsPointLocator::Match &snapMatch : lockedSnapVertices )
344 {
345 const QgsPointXY point = snapMatch.point();
346 const QPointF canvasPoint = toCanvasCoordinates( point );
347
348 painter->drawLine( canvasPoint + QPointF( 5, 5 ), canvasPoint - QPointF( 5, 5 ) );
349 painter->drawLine( canvasPoint + QPointF( -5, 5 ), canvasPoint - QPointF( -5, 5 ) );
350 }
351
352 if ( !lockedSnapVertices.isEmpty() )
353 {
354 const QgsPointXY point = lockedSnapVertices.last().point();
355 const QPointF canvasPoint = toCanvasCoordinates( point );
356
357 painter->drawLine( canvasPoint + QPointF( 0, 5 ), canvasPoint - QPointF( 0, 5 ) );
358 painter->drawLine( canvasPoint + QPointF( 5, 0 ), canvasPoint - QPointF( 5, 0 ) );
359 }
360}
361
363{
364 // Use visible polygon rather than extent to properly handle rotated maps
365 QPolygonF mapPoly = mMapCanvas->mapSettings().visiblePolygon();
366 const double canvasWidth = QLineF( mapPoly[0], mapPoly[1] ).length();
367 const double canvasHeight = QLineF( mapPoly[0], mapPoly[3] ).length();
368 const QgsRectangle mapRect = QgsRectangle( mapPoly[0], QgsPointXY( mapPoly[0].x() + canvasWidth, mapPoly[0].y() - canvasHeight ) );
369 if ( rect() != mapRect )
370 setRect( mapRect );
371}
@ 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.
QgsAdvancedDigitizingTool * tool() const
Returns the current advanced digitizing tool.
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.
An abstract class for advanced digitizing tools.
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:165
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:738
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