QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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
17
19#include "qgsmapcanvas.h"
20
21#include <QPainter>
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
36{
37 if ( !mAdvancedDigitizingDockWidget->cadEnabled() )
38 return;
39
40 painter->setRenderHint( QPainter::Antialiasing );
41 painter->setCompositionMode( QPainter::CompositionMode_Difference );
42
43 // Draw construction guides
44 if ( mAdvancedDigitizingDockWidget->showConstructionGuides() )
45 {
46 if ( QgsVectorLayer *constructionGuidesLayer = mAdvancedDigitizingDockWidget->constructionGuidesLayer() )
47 {
48 QgsFeatureIterator it = constructionGuidesLayer->getFeatures( QgsFeatureRequest().setNoAttributes().setFilterRect( mMapCanvas->mapSettings().visibleExtent() ) );
49 QgsFeature feature;
50 painter->setPen( mConstructionGuidesPen );
51 while ( it.nextFeature( feature ) )
52 {
53 QgsGeometry geom = feature.geometry();
54 geom.mapToPixel( *mMapCanvas->getCoordinateTransform() );
55 const QPolygonF polygon = geom.asQPolygonF();
56 painter->drawPolyline( polygon );
57 }
58 }
59 }
60
61 // Draw current tool
62 if ( QgsAdvancedDigitizingTool *tool = mAdvancedDigitizingDockWidget->tool() )
63 {
64 // if a tool is active in the dock, then delegate to that tool to handle decorating the canvas instead of using the default decorations
65 tool->paint( painter );
66 return;
67 }
68
69 // Use visible polygon rather than extent to properly handle rotated maps
70 QPolygonF mapPoly = mMapCanvas->mapSettings().visiblePolygon();
71 const double canvasWidth = QLineF( mapPoly[0], mapPoly[1] ).length();
72 const double canvasHeight = QLineF( mapPoly[0], mapPoly[3] ).length();
73
74 const int nPoints = mAdvancedDigitizingDockWidget->pointsCount();
75 if ( !nPoints )
76 return;
77
78 bool previousPointExist, penulPointExist;
79 const QgsPointXY curPoint = mAdvancedDigitizingDockWidget->currentPointV2();
80 const QgsPointXY prevPoint = mAdvancedDigitizingDockWidget->previousPointV2( &previousPointExist );
81 const QgsPointXY penulPoint = mAdvancedDigitizingDockWidget->penultimatePointV2( &penulPointExist );
82 const bool snappedToVertex = mAdvancedDigitizingDockWidget->snappedToVertex();
83 const QList<QgsPointXY> snappedSegment = mAdvancedDigitizingDockWidget->snappedSegment();
84 const bool hasSnappedSegment = snappedSegment.count() == 2;
85
86 const bool curPointExist = mapPoly.containsPoint( curPoint.toQPointF(), Qt::OddEvenFill );
87
88 const double mupp = mMapCanvas->getCoordinateTransform()->mapUnitsPerPixel();
89 if ( mupp == 0 )
90 return;
91
92 const double canvasRotationRad = mMapCanvas->rotation() * M_PI / 180;
93 const double canvasDiagonalDimension = ( canvasWidth + canvasHeight ) / mupp;
94
95 QPointF curPointPix, prevPointPix, penulPointPix, snapSegmentPix1, snapSegmentPix2;
96
97 if ( curPointExist )
98 {
99 curPointPix = toCanvasCoordinates( curPoint );
100 }
101 if ( previousPointExist )
102 {
103 prevPointPix = toCanvasCoordinates( prevPoint );
104 }
105 if ( penulPointExist )
106 {
107 penulPointPix = toCanvasCoordinates( penulPoint );
108 }
109 if ( hasSnappedSegment )
110 {
111 snapSegmentPix1 = toCanvasCoordinates( snappedSegment[0] );
112 snapSegmentPix2 = toCanvasCoordinates( snappedSegment[1] );
113 }
114
115 // Draw point snap
116 if ( curPointExist && snappedToVertex )
117 {
118 painter->setPen( mSnapPen );
119 painter->drawEllipse( curPointPix, 10, 10 );
120 }
121
122 // Draw segment snap
123 if ( hasSnappedSegment && !snappedToVertex )
124 {
125 painter->setPen( mSnapPen );
126 painter->drawLine( snapSegmentPix1, snapSegmentPix2 );
127
128 if ( curPointExist )
129 {
130 painter->setPen( mSnapLinePen );
131 painter->drawLine( snapSegmentPix1, curPointPix );
132 }
133 }
134
135 // Draw segment par/per input
136 if ( mAdvancedDigitizingDockWidget->betweenLineConstraint() != Qgis::BetweenLineConstraint::NoConstraint && hasSnappedSegment )
137 {
138 painter->setPen( mConstruction2Pen );
139 painter->drawLine( snapSegmentPix1, snapSegmentPix2 );
140 }
141
142 // Draw angle
143 if ( nPoints > 1 )
144 {
145 double a0, a;
146 if ( mAdvancedDigitizingDockWidget->constraintAngle()->relative() && nPoints > 2 )
147 {
148 a0 = std::atan2( -( prevPoint.y() - penulPoint.y() ), prevPoint.x() - penulPoint.x() );
149 }
150 else
151 {
152 a0 = 0;
153 }
154 if ( mAdvancedDigitizingDockWidget->constraintAngle()->isLocked() )
155 {
156 a = a0 - mAdvancedDigitizingDockWidget->constraintAngle()->value() * M_PI / 180;
157 }
158 else
159 {
160 a = std::atan2( -( curPoint.y() - prevPoint.y() ), curPoint.x() - prevPoint.x() );
161 }
162
163 a0 += canvasRotationRad;
164 a += canvasRotationRad;
165
166 painter->setPen( mConstruction2Pen );
167 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 ) );
168 painter->drawLine( prevPointPix, prevPointPix + 60 * QPointF( std::cos( a0 ), std::sin( a0 ) ) );
169
170
171 if ( mAdvancedDigitizingDockWidget->constraintAngle()->isLocked() )
172 {
173 painter->setPen( mLockedPen );
174 const double canvasPadding = QLineF( prevPointPix, curPointPix ).length();
175 painter
176 ->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(
217 toCanvasCoordinates( QgsPointXY( x, mapPoly[0].y() ) ) - canvasDiagonalDimension * QPointF( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) ),
218 toCanvasCoordinates( QgsPointXY( x, mapPoly[0].y() ) ) + canvasDiagonalDimension * QPointF( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) )
219 );
220 }
221 }
222
223 // Draw y
224 if ( mAdvancedDigitizingDockWidget->constraintY()->isLocked() )
225 {
226 double y = 0.0;
227 bool draw = true;
228 painter->setPen( mLockedPen );
229 if ( mAdvancedDigitizingDockWidget->constraintY()->relative() )
230 {
231 if ( nPoints > 1 )
232 {
233 y = mAdvancedDigitizingDockWidget->constraintY()->value() + prevPoint.y();
234 }
235 else
236 {
237 draw = false;
238 }
239 }
240 else
241 {
242 y = mAdvancedDigitizingDockWidget->constraintY()->value();
243 }
244 if ( draw )
245 {
246 painter->drawLine(
247 toCanvasCoordinates( QgsPointXY( mapPoly[0].x(), y ) ) - canvasDiagonalDimension * QPointF( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) ),
248 toCanvasCoordinates( QgsPointXY( mapPoly[0].x(), y ) ) + canvasDiagonalDimension * QPointF( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) )
249 );
250 }
251 }
252
253 // Draw constraints
254 if ( mAdvancedDigitizingDockWidget->betweenLineConstraint() == Qgis::BetweenLineConstraint::NoConstraint )
255 {
256 if ( curPointExist && previousPointExist )
257 {
258 painter->setPen( mConstruction2Pen );
259 painter->drawLine( prevPointPix, curPointPix );
260 }
261
262 if ( previousPointExist && penulPointExist )
263 {
264 painter->setPen( mConstruction1Pen );
265 painter->drawLine( penulPointPix, prevPointPix );
266 }
267 }
268
269 if ( curPointExist )
270 {
271 painter->setPen( mCursorPen );
272 painter->drawLine( curPointPix + QPointF( -5, -5 ), curPointPix + QPointF( +5, +5 ) );
273 painter->drawLine( curPointPix + QPointF( -5, +5 ), curPointPix + QPointF( +5, -5 ) );
274 }
275
276 auto lineExtensionSide = mAdvancedDigitizingDockWidget->lineExtensionSide();
277 if ( mAdvancedDigitizingDockWidget->constraintLineExtension()->isLocked() && lineExtensionSide != Qgis::LineExtensionSide::NoVertex && !mAdvancedDigitizingDockWidget->lockedSnapVertices().isEmpty() )
278 {
279 painter->setPen( mLockedPen );
280
281 const QgsPointLocator::Match snap = mAdvancedDigitizingDockWidget->lockedSnapVertices().constLast();
282 const QPointF snappedPoint = toCanvasCoordinates( snap.point() );
283
285 req.setFilterFid( snap.featureId() );
286 req.setNoAttributes();
287 req.setDestinationCrs( mMapCanvas->mapSettings().destinationCrs(), mMapCanvas->mapSettings().transformContext() );
288 QgsFeatureIterator featureIt = snap.layer()->getFeatures( req );
289
290 QgsFeature feature;
291 featureIt.nextFeature( feature );
292
293 const QgsGeometry geometry = feature.geometry();
294 const QgsAbstractGeometry *geom = geometry.constGet();
295
296 QgsPoint vertex;
297 QgsVertexId vertexId;
298 geometry.vertexIdFromVertexNr( snap.vertexIndex(), vertexId );
299 if ( vertexId.isValid() )
300 {
301 QgsVertexId previousVertexId;
302 QgsVertexId nextVertexId;
303 geom->adjacentVertices( vertexId, previousVertexId, nextVertexId );
304
305 if ( lineExtensionSide == Qgis::LineExtensionSide::BeforeVertex )
306 {
307 vertex = geom->vertexAt( previousVertexId );
308 }
309 else
310 {
311 vertex = geom->vertexAt( nextVertexId );
312 }
313 }
314
315 if ( !vertex.isEmpty() )
316 {
317 const QPointF point = toCanvasCoordinates( vertex );
318 const double angle = std::atan2( snappedPoint.y() - point.y(), snappedPoint.x() - point.x() );
319
320 const double canvasPadding = QLineF( snappedPoint, curPointPix ).length();
321 painter
322 ->drawLine( snappedPoint + ( canvasPadding - canvasDiagonalDimension ) * QPointF( std::cos( angle ), std::sin( angle ) ), snappedPoint + ( canvasPadding + canvasDiagonalDimension ) * QPointF( std::cos( angle ), std::sin( angle ) ) );
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, toCanvasCoordinates( point ) + canvasDiagonalDimension * rotation );
336 }
337
338 coordinateExtension = mAdvancedDigitizingDockWidget->softLockY();
339 if ( coordinateExtension != std::numeric_limits<double>::quiet_NaN() )
340 {
341 const QgsPointXY point( mapPoly[0].x(), coordinateExtension );
342 const QPointF rotation( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) );
343 painter->drawLine( toCanvasCoordinates( point ) - canvasDiagonalDimension * rotation, toCanvasCoordinates( point ) + canvasDiagonalDimension * rotation );
344 }
345 }
346
347 painter->setPen( mCursorPen );
348
349 const QList<QgsPointLocator::Match> lockedSnapVertices = mAdvancedDigitizingDockWidget->lockedSnapVertices();
350 for ( const QgsPointLocator::Match &snapMatch : lockedSnapVertices )
351 {
352 const QgsPointXY point = snapMatch.point();
353 const QPointF canvasPoint = toCanvasCoordinates( point );
354
355 painter->drawLine( canvasPoint + QPointF( 5, 5 ), canvasPoint - QPointF( 5, 5 ) );
356 painter->drawLine( canvasPoint + QPointF( -5, 5 ), canvasPoint - QPointF( -5, 5 ) );
357 }
358
359 if ( !lockedSnapVertices.isEmpty() )
360 {
361 const QgsPointXY point = lockedSnapVertices.last().point();
362 const QPointF canvasPoint = toCanvasCoordinates( point );
363
364 painter->drawLine( canvasPoint + QPointF( 0, 5 ), canvasPoint - QPointF( 0, 5 ) );
365 painter->drawLine( canvasPoint + QPointF( 5, 0 ), canvasPoint - QPointF( 5, 0 ) );
366 }
367}
368
370{
371 // Use visible polygon rather than extent to properly handle rotated maps
372 QPolygonF mapPoly = mMapCanvas->mapSettings().visiblePolygon();
373 const double canvasWidth = QLineF( mapPoly[0], mapPoly[1] ).length();
374 const double canvasHeight = QLineF( mapPoly[0], mapPoly[3] ).length();
375 const QgsRectangle mapRect = QgsRectangle( mapPoly[0], QgsPointXY( mapPoly[0].x() + canvasWidth, mapPoly[0].y() - canvasHeight ) );
376 if ( rect() != mapRect )
377 setRect( mapRect );
378}
@ NoConstraint
No additional constraint.
Definition qgis.h:4166
@ NoVertex
Don't lock to vertex.
Definition qgis.h:4181
@ BeforeVertex
Lock to previous vertex.
Definition qgis.h:4179
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)
A dockable widget used to handle the CAD tools on top of a selection of map tools.
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.
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:60
QgsGeometry geometry
Definition qgsfeature.h:71
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.
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
QgsMapCanvasItem(QgsMapCanvas *mapCanvas)
protected constructor: cannot be constructed directly
Map canvas is a class for displaying all GIS data types on a canvas.
Represents a 2D point.
Definition qgspointxy.h:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
QPointF toQPointF() const
Converts a point to a QPointF.
Definition qgspointxy.h:168
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
bool isEmpty() const override
Returns true if the geometry is empty.
Definition qgspoint.cpp:766
A rectangle specified with double values.
Represents a vector layer which manages a vector based dataset.
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:34
bool isValid() const
Returns true if the vertex id is valid.
Definition qgsvertexid.h:50