QGIS API Documentation 3.41.0-Master (45a0abf3bec)
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
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,
169 prevPointPix.y() - 20,
170 40, 40 ),
171 static_cast<int>( 16 * -a0 * 180 / M_PI ),
172 static_cast<int>( 16 * ( a0 - a ) * 180 / M_PI ) );
173 painter->drawLine( prevPointPix,
174 prevPointPix + 60 * QPointF( std::cos( a0 ), std::sin( a0 ) ) );
175
176
177 if ( mAdvancedDigitizingDockWidget->constraintAngle()->isLocked() )
178 {
179 painter->setPen( mLockedPen );
180 const double canvasPadding = QLineF( prevPointPix, curPointPix ).length();
181 painter->drawLine( prevPointPix + ( canvasPadding - canvasDiagonalDimension ) * QPointF( std::cos( a ), std::sin( a ) ),
182 prevPointPix + ( canvasPadding + canvasDiagonalDimension ) * QPointF( std::cos( a ), std::sin( a ) ) );
183 }
184 }
185
186 // Draw distance
187 if ( nPoints > 1 && mAdvancedDigitizingDockWidget->constraintDistance()->isLocked() )
188 {
189 painter->setPen( mLockedPen );
190 const double r = mAdvancedDigitizingDockWidget->constraintDistance()->value() / mupp;
191 QPainterPath ellipsePath;
192 ellipsePath.addEllipse( prevPointPix, r, r );
193 const double a = std::atan2( -( curPoint.y() - prevPoint.y() ), curPoint.x() - prevPoint.x() ) + canvasRotationRad;
194 const QTransform t = QTransform().translate( prevPointPix.x(), prevPointPix.y() ).rotateRadians( a ).translate( -prevPointPix.x(), -prevPointPix.y() );
195 const QPolygonF ellipsePoly = ellipsePath.toFillPolygon( t );
196 painter->drawPolygon( ellipsePoly );
197 }
198
199 // Draw x
200 if ( mAdvancedDigitizingDockWidget->constraintX()->isLocked() )
201 {
202 double x = 0.0;
203 bool draw = true;
204 painter->setPen( mLockedPen );
205 if ( mAdvancedDigitizingDockWidget->constraintX()->relative() )
206 {
207 if ( nPoints > 1 )
208 {
209 x = mAdvancedDigitizingDockWidget->constraintX()->value() + prevPoint.x();
210 }
211 else
212 {
213 draw = false;
214 }
215 }
216 else
217 {
218 x = mAdvancedDigitizingDockWidget->constraintX()->value();
219 }
220 if ( draw )
221 {
222 painter->drawLine( toCanvasCoordinates( QgsPointXY( x, mapPoly[0].y() ) ) - canvasDiagonalDimension * QPointF( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) ),
223 toCanvasCoordinates( QgsPointXY( x, mapPoly[0].y() ) ) + canvasDiagonalDimension * QPointF( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) ) );
224 }
225 }
226
227 // Draw y
228 if ( mAdvancedDigitizingDockWidget->constraintY()->isLocked() )
229 {
230 double y = 0.0;
231 bool draw = true;
232 painter->setPen( mLockedPen );
233 if ( mAdvancedDigitizingDockWidget->constraintY()->relative() )
234 {
235 if ( nPoints > 1 )
236 {
237 y = mAdvancedDigitizingDockWidget->constraintY()->value() + prevPoint.y();
238 }
239 else
240 {
241 draw = false;
242 }
243 }
244 else
245 {
246 y = mAdvancedDigitizingDockWidget->constraintY()->value();
247 }
248 if ( draw )
249 {
250 painter->drawLine( toCanvasCoordinates( QgsPointXY( mapPoly[0].x(), y ) ) - canvasDiagonalDimension * QPointF( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) ),
251 toCanvasCoordinates( QgsPointXY( mapPoly[0].x(), y ) ) + canvasDiagonalDimension * QPointF( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) ) );
252
253 }
254 }
255
256 // Draw constraints
257 if ( mAdvancedDigitizingDockWidget->betweenLineConstraint() == Qgis::BetweenLineConstraint::NoConstraint )
258 {
259 if ( curPointExist && previousPointExist )
260 {
261 painter->setPen( mConstruction2Pen );
262 painter->drawLine( prevPointPix, curPointPix );
263 }
264
265 if ( previousPointExist && penulPointExist )
266 {
267 painter->setPen( mConstruction1Pen );
268 painter->drawLine( penulPointPix, prevPointPix );
269 }
270 }
271
272 if ( curPointExist )
273 {
274 painter->setPen( mCursorPen );
275 painter->drawLine( curPointPix + QPointF( -5, -5 ),
276 curPointPix + QPointF( +5, +5 ) );
277 painter->drawLine( curPointPix + QPointF( -5, +5 ),
278 curPointPix + QPointF( +5, -5 ) );
279 }
280
281 auto lineExtensionSide = mAdvancedDigitizingDockWidget->lineExtensionSide();
282 if ( mAdvancedDigitizingDockWidget->constraintLineExtension()->isLocked() &&
283 lineExtensionSide != Qgis::LineExtensionSide::NoVertex &&
284 !mAdvancedDigitizingDockWidget->lockedSnapVertices().isEmpty() )
285 {
286 painter->setPen( mLockedPen );
287
288 const QgsPointLocator::Match snap = mAdvancedDigitizingDockWidget->lockedSnapVertices().constLast();
289 const QPointF snappedPoint = toCanvasCoordinates( snap.point() );
290
292 req.setFilterFid( snap.featureId() );
293 req.setNoAttributes();
295 QgsFeatureIterator featureIt = snap.layer()->getFeatures( req );
296
297 QgsFeature feature;
298 featureIt.nextFeature( feature );
299
300 const QgsGeometry geometry = feature.geometry();
301 const QgsAbstractGeometry *geom = geometry.constGet();
302
303 QgsPoint vertex;
304 QgsVertexId vertexId;
305 geometry.vertexIdFromVertexNr( snap.vertexIndex(), vertexId );
306 if ( vertexId.isValid() )
307 {
308 QgsVertexId previousVertexId;
309 QgsVertexId nextVertexId;
310 geom->adjacentVertices( vertexId, previousVertexId, nextVertexId );
311
312 if ( lineExtensionSide == Qgis::LineExtensionSide::BeforeVertex )
313 {
314 vertex = geom->vertexAt( previousVertexId );
315 }
316 else
317 {
318 vertex = geom->vertexAt( nextVertexId );
319 }
320 }
321
322 if ( !vertex.isEmpty() )
323 {
324 const QPointF point = toCanvasCoordinates( vertex );
325 const double angle = std::atan2( snappedPoint.y() - point.y(), snappedPoint.x() - point.x() );
326
327 const double canvasPadding = QLineF( snappedPoint, curPointPix ).length();
328 painter->drawLine( snappedPoint + ( canvasPadding - canvasDiagonalDimension ) * QPointF( std::cos( angle ), std::sin( angle ) ),
329 snappedPoint + ( canvasPadding + canvasDiagonalDimension ) * QPointF( std::cos( angle ), std::sin( angle ) ) );
330 }
331
332 }
333
334 if ( mAdvancedDigitizingDockWidget->constraintXyVertex()->isLocked() )
335 {
336 painter->setPen( mLockedPen );
337
338 double coordinateExtension = mAdvancedDigitizingDockWidget->softLockX();
339 if ( coordinateExtension != std::numeric_limits<double>::quiet_NaN() )
340 {
341 const QgsPointXY point( coordinateExtension, mapPoly[0].y() );
342 const QPointF rotation( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) );
343 painter->drawLine( toCanvasCoordinates( point ) - canvasDiagonalDimension * rotation,
344 toCanvasCoordinates( point ) + canvasDiagonalDimension * rotation );
345 }
346
347 coordinateExtension = mAdvancedDigitizingDockWidget->softLockY();
348 if ( coordinateExtension != std::numeric_limits<double>::quiet_NaN() )
349 {
350 const QgsPointXY point( mapPoly[0].x(), coordinateExtension );
351 const QPointF rotation( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) );
352 painter->drawLine( toCanvasCoordinates( point ) - canvasDiagonalDimension * rotation,
353 toCanvasCoordinates( point ) + canvasDiagonalDimension * rotation );
354 }
355 }
356
357 painter->setPen( mCursorPen );
358
359 const QList< QgsPointLocator::Match > lockedSnapVertices = mAdvancedDigitizingDockWidget->lockedSnapVertices();
360 for ( const QgsPointLocator::Match &snapMatch : lockedSnapVertices )
361 {
362 const QgsPointXY point = snapMatch.point();
363 const QPointF canvasPoint = toCanvasCoordinates( point );
364
365 painter->drawLine( canvasPoint + QPointF( 5, 5 ),
366 canvasPoint - QPointF( 5, 5 ) );
367 painter->drawLine( canvasPoint + QPointF( -5, 5 ),
368 canvasPoint - QPointF( -5, 5 ) );
369 }
370
371 if ( !lockedSnapVertices.isEmpty() )
372 {
373 const QgsPointXY point = lockedSnapVertices.last().point();
374 const QPointF canvasPoint = toCanvasCoordinates( point );
375
376 painter->drawLine( canvasPoint + QPointF( 0, 5 ),
377 canvasPoint - QPointF( 0, 5 ) );
378 painter->drawLine( canvasPoint + QPointF( 5, 0 ),
379 canvasPoint - QPointF( 5, 0 ) );
380 }
381}
382
384{
385 // Use visible polygon rather than extent to properly handle rotated maps
386 QPolygonF mapPoly = mMapCanvas->mapSettings().visiblePolygon();
387 const double canvasWidth = QLineF( mapPoly[0], mapPoly[1] ).length();
388 const double canvasHeight = QLineF( mapPoly[0], mapPoly[3] ).length();
389 const QgsRectangle mapRect = QgsRectangle( mapPoly[0],
391 mapPoly[0].x() + canvasWidth,
392 mapPoly[0].y() - canvasHeight
393 )
394 );
395 if ( rect() != mapRect )
396 setRect( mapRect );
397}
@ 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