QGIS API Documentation  3.25.0-Master (10b47c2603)
qgscadutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscadutils.cpp
3  -------------------
4  begin : September 2017
5  copyright : (C) 2017 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************/
8 /***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 
17 #include <QQueue>
18 
19 #include "qgscadutils.h"
20 
21 #include "qgslogger.h"
22 #include "qgssnappingutils.h"
23 #include "qgsgeometryutils.h"
24 
25 // tolerances for soft constraints (last values, and common angles)
26 // for angles, both tolerance in pixels and degrees are used for better performance
27 static const double SOFT_CONSTRAINT_TOLERANCE_PIXEL = 15;
28 static const double SOFT_CONSTRAINT_TOLERANCE_DEGREES = 10;
29 
30 
32 struct EdgesOnlyFilter : public QgsPointLocator::MatchFilter
33 {
34  bool acceptMatch( const QgsPointLocator::Match &m ) override { return m.hasEdge(); }
35 };
37 
38 
40 {
42  res.valid = true;
43  res.softLockCommonAngle = -1;
44 
46  res.softLockX = std::numeric_limits<double>::quiet_NaN();
47  res.softLockY = std::numeric_limits<double>::quiet_NaN();
48 
49  // try to snap to anything
50  const QgsPointLocator::Match snapMatch = ctx.snappingUtils->snapToMap( originalMapPoint, nullptr, true );
51  res.snapMatch = snapMatch;
52  QgsPointXY point = snapMatch.isValid() ? snapMatch.point() : originalMapPoint;
53  QgsPointXY edgePt0, edgePt1;
54  if ( snapMatch.hasEdge() )
55  {
56  snapMatch.edgePoints( edgePt0, edgePt1 );
57  // note : res.edgeMatch should be removed, as we can just check snapMatch.hasEdge()
58  res.edgeMatch = snapMatch;
59  }
60  else
61  {
63  }
64 
65  int numberOfHardLock = 0;
66  if ( ctx.xConstraint.locked ) ++numberOfHardLock;
67  if ( ctx.yConstraint.locked ) ++numberOfHardLock;
68  if ( ctx.angleConstraint.locked ) ++numberOfHardLock;
69  if ( ctx.distanceConstraint.locked ) ++numberOfHardLock;
70 
71  QgsPointXY previousPt, penultimatePt;
72  if ( ctx.cadPoints().count() >= 2 )
73  previousPt = ctx.cadPoint( 1 );
74  if ( ctx.cadPoints().count() >= 3 )
75  penultimatePt = ctx.cadPoint( 2 );
76 
77  // *****************************
78  // ---- X constraint
79  if ( ctx.xConstraint.locked )
80  {
81  if ( !ctx.xConstraint.relative )
82  {
83  point.setX( ctx.xConstraint.value );
84  }
85  else if ( ctx.cadPoints().count() >= 2 )
86  {
87  point.setX( previousPt.x() + ctx.xConstraint.value );
88  }
89  if ( snapMatch.hasEdge() && !ctx.yConstraint.locked )
90  {
91  // intersect with snapped segment line at X coordinate
92  const double dx = edgePt1.x() - edgePt0.x();
93  if ( dx == 0 )
94  {
95  point.setY( edgePt0.y() );
96  }
97  else
98  {
99  const double dy = edgePt1.y() - edgePt0.y();
100  point.setY( edgePt0.y() + ( dy * ( point.x() - edgePt0.x() ) ) / dx );
101  }
102  }
103  }
104  else if ( numberOfHardLock < 2 && ctx.xyVertexConstraint.locked )
105  {
106  for ( QgsPointLocator::Match snapMatch : ctx.lockedSnapVertices() )
107  {
108  const QgsPointXY vertex = snapMatch.point();
109  if ( vertex.isEmpty() )
110  continue;
111 
112  if ( std::abs( point.x() - vertex.x() ) / ctx.mapUnitsPerPixel < SOFT_CONSTRAINT_TOLERANCE_PIXEL )
113  {
114  point.setX( vertex.x() );
115  res.softLockX = vertex.x();
116  }
117  }
118  }
119 
120  // *****************************
121  // ---- Y constraint
122  if ( ctx.yConstraint.locked )
123  {
124  if ( !ctx.yConstraint.relative )
125  {
126  point.setY( ctx.yConstraint.value );
127  }
128  else if ( ctx.cadPoints().count() >= 2 )
129  {
130  point.setY( previousPt.y() + ctx.yConstraint.value );
131  }
132  if ( snapMatch.hasEdge() && !ctx.xConstraint.locked )
133  {
134  // intersect with snapped segment line at Y coordinate
135  const double dy = edgePt1.y() - edgePt0.y();
136  if ( dy == 0 )
137  {
138  point.setX( edgePt0.x() );
139  }
140  else
141  {
142  const double dx = edgePt1.x() - edgePt0.x();
143  point.setX( edgePt0.x() + ( dx * ( point.y() - edgePt0.y() ) ) / dy );
144  }
145  }
146  }
147  else if ( numberOfHardLock < 2 && ctx.xyVertexConstraint.locked )
148  {
149  for ( QgsPointLocator::Match snapMatch : ctx.lockedSnapVertices() )
150  {
151  const QgsPointXY vertex = snapMatch.point();
152  if ( vertex.isEmpty() )
153  continue;
154 
155  if ( std::abs( point.y() - vertex.y() ) / ctx.mapUnitsPerPixel < SOFT_CONSTRAINT_TOLERANCE_PIXEL )
156  {
157  point.setY( vertex.y() );
158  res.softLockY = vertex.y();
159  }
160  }
161  }
162 
163  // *****************************
164  // ---- Common Angle constraint
165  if ( numberOfHardLock < 2 && !ctx.angleConstraint.locked && ctx.cadPoints().count() >= 2 && ctx.commonAngleConstraint.locked && ctx.commonAngleConstraint.value != 0 )
166  {
167  const double commonAngle = ctx.commonAngleConstraint.value * M_PI / 180;
168  // see if soft common angle constraint should be performed
169  // only if not in HardLock mode
170  double softAngle = std::atan2( point.y() - previousPt.y(),
171  point.x() - previousPt.x() );
172  double deltaAngle = 0;
173  if ( ctx.commonAngleConstraint.relative && ctx.cadPoints().count() >= 3 )
174  {
175  // compute the angle relative to the last segment (0° is aligned with last segment)
176  deltaAngle = std::atan2( previousPt.y() - penultimatePt.y(),
177  previousPt.x() - penultimatePt.x() );
178  softAngle -= deltaAngle;
179  }
180  const int quo = std::round( softAngle / commonAngle );
181  if ( std::fabs( softAngle - quo * commonAngle ) * 180.0 * M_1_PI <= SOFT_CONSTRAINT_TOLERANCE_DEGREES )
182  {
183  // also check the distance in pixel to the line, otherwise it's too sticky at long ranges
184  softAngle = quo * commonAngle;
185  // http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html
186  // use the direction vector (cos(a),sin(a)) from previous point. |x2-x1|=1 since sin2+cos2=1
187  const double dist = std::fabs( std::cos( softAngle + deltaAngle ) * ( previousPt.y() - point.y() )
188  - std::sin( softAngle + deltaAngle ) * ( previousPt.x() - point.x() ) );
189  if ( dist / ctx.mapUnitsPerPixel < SOFT_CONSTRAINT_TOLERANCE_PIXEL )
190  {
191  res.softLockCommonAngle = 180.0 / M_PI * softAngle;
192  }
193  }
194  }
195 
196  // angle can be locked in one of the two ways:
197  // 1. "hard" lock defined by the user
198  // 2. "soft" lock from common angle (e.g. 45 degrees)
199  bool angleLocked = false, angleRelative = false;
200  double angleValueDeg = 0;
201  if ( ctx.angleConstraint.locked )
202  {
203  angleLocked = true;
204  angleRelative = ctx.angleConstraint.relative;
205  angleValueDeg = ctx.angleConstraint.value;
206  }
207  else if ( res.softLockCommonAngle != -1 )
208  {
209  angleLocked = true;
210  angleRelative = ctx.commonAngleConstraint.relative;
211  angleValueDeg = res.softLockCommonAngle;
212  }
213 
214  // *****************************
215  // ---- Angle constraint
216  // input angles are in degrees
217  if ( angleLocked )
218  {
219  double angleValue = angleValueDeg * M_PI / 180;
220  if ( angleRelative && ctx.cadPoints().count() >= 3 )
221  {
222  // compute the angle relative to the last segment (0° is aligned with last segment)
223  angleValue += std::atan2( previousPt.y() - penultimatePt.y(),
224  previousPt.x() - penultimatePt.x() );
225  }
226 
227  const double cosa = std::cos( angleValue );
228  const double sina = std::sin( angleValue );
229  const double v = ( point.x() - previousPt.x() ) * cosa + ( point.y() - previousPt.y() ) * sina;
230  if ( ctx.xConstraint.locked && ctx.yConstraint.locked )
231  {
232  // do nothing if both X,Y are already locked
233  }
234  else if ( ctx.xConstraint.locked || !std::isnan( res.softLockX ) )
235  {
236  if ( qgsDoubleNear( cosa, 0.0 ) )
237  {
238  res.valid = false;
239  }
240  else
241  {
242  double x = ctx.xConstraint.value;
243  if ( !ctx.xConstraint.relative )
244  {
245  x -= previousPt.x();
246  }
247  point.setY( previousPt.y() + x * sina / cosa );
248  }
249  }
250  else if ( ctx.yConstraint.locked || !std::isnan( res.softLockY ) )
251  {
252  if ( qgsDoubleNear( sina, 0.0 ) )
253  {
254  res.valid = false;
255  }
256  else
257  {
258  double y = ctx.yConstraint.value;
259  if ( !ctx.yConstraint.relative )
260  {
261  y -= previousPt.y();
262  }
263  point.setX( previousPt.x() + y * cosa / sina );
264  }
265  }
266  else
267  {
268  point.setX( previousPt.x() + cosa * v );
269  point.setY( previousPt.y() + sina * v );
270  }
271 
272  if ( snapMatch.hasEdge() && !ctx.distanceConstraint.locked )
273  {
274  // magnetize to the intersection of the snapped segment and the lockedAngle
275 
276  // line of previous point + locked angle
277  const double x1 = previousPt.x();
278  const double y1 = previousPt.y();
279  const double x2 = previousPt.x() + cosa;
280  const double y2 = previousPt.y() + sina;
281  // line of snapped segment
282  const double x3 = edgePt0.x();
283  const double y3 = edgePt0.y();
284  const double x4 = edgePt1.x();
285  const double y4 = edgePt1.y();
286 
287  const double d = ( x1 - x2 ) * ( y3 - y4 ) - ( y1 - y2 ) * ( x3 - x4 );
288 
289  // do not compute intersection if lines are almost parallel
290  // this threshold might be adapted
291  if ( std::fabs( d ) > 0.01 )
292  {
293  point.setX( ( ( x3 - x4 ) * ( x1 * y2 - y1 * x2 ) - ( x1 - x2 ) * ( x3 * y4 - y3 * x4 ) ) / d );
294  point.setY( ( ( y3 - y4 ) * ( x1 * y2 - y1 * x2 ) - ( y1 - y2 ) * ( x3 * y4 - y3 * x4 ) ) / d );
295  }
296  }
297  }
298 
299  // *****************************
300  // ---- Line Extension Constraint
301 
302  if ( numberOfHardLock < 2 && ctx.lineExtensionConstraint.locked && ctx.lockedSnapVertices().length() != 0 )
303  {
304  const QgsPointLocator::Match snap = ctx.lockedSnapVertices().last();
305  const QgsPointXY extensionPoint = snap.point();
306 
307  if ( snap.layer() && !extensionPoint.isEmpty() )
308  {
309  auto checkLineExtension = [&]( QgsPoint vertex )
310  {
311  if ( vertex.isEmpty() )
312  {
313  return false;
314  }
315 
316  const double distance = QgsGeometryUtils::distToInfiniteLine(
317  QgsPoint( point ), QgsPoint( extensionPoint ), vertex );
318 
319  if ( distance / ctx.mapUnitsPerPixel < SOFT_CONSTRAINT_TOLERANCE_PIXEL )
320  {
321  if ( ctx.xConstraint.locked || !std::isnan( res.softLockX ) )
322  {
323  QgsPoint intersection;
324  const bool intersect = QgsGeometryUtils::lineIntersection(
325  QgsPoint( point ), QgsVector( 0, 1 ),
326  QgsPoint( extensionPoint ), QgsPoint( extensionPoint ) - vertex,
327  intersection
328  );
329  if ( intersect )
330  {
331  point = QgsPointXY( intersection );
332  }
333  }
334  else if ( ctx.yConstraint.locked || !std::isnan( res.softLockY ) )
335  {
336  QgsPoint intersection;
337  const bool intersect = QgsGeometryUtils::lineIntersection(
338  QgsPoint( point ), QgsVector( 1, 0 ),
339  QgsPoint( extensionPoint ), QgsPoint( extensionPoint ) - vertex,
340  intersection
341  );
342  if ( intersect )
343  {
344  point = QgsPointXY( intersection );
345  }
346  }
347  else if ( angleLocked )
348  {
349  const double angleValue = angleValueDeg * M_PI / 180;
350  const double cosa = std::cos( angleValue );
351  const double sina = std::sin( angleValue );
352 
353  QgsPoint intersection;
355  QgsPoint( previousPt ), QgsVector( cosa, sina ),
356  QgsPoint( extensionPoint ), QgsPoint( extensionPoint ) - vertex,
357  intersection
358  );
359  point = QgsPointXY( intersection );
360  }
361  else
362  {
363  double angleValue = std::atan2( extensionPoint.y() - vertex.y(),
364  extensionPoint.x() - vertex.x() );
365 
366  const double cosa = std::cos( angleValue );
367  const double sina = std::sin( angleValue );
368  const double v = ( point.x() - extensionPoint.x() ) * cosa + ( point.y() - extensionPoint.y() ) * sina;
369 
370  point.setX( extensionPoint.x() + cosa * v );
371  point.setY( extensionPoint.y() + sina * v );
372  }
373 
374  return true;
375  }
376  return false;
377  };
378 
379  const QgsFeature feature = snap.layer()->getFeature( snap.featureId() );
380  const QgsGeometry geom = feature.geometry();
381 
382  bool checked = checkLineExtension( geom.vertexAt( snap.vertexIndex() - 1 ) );
383  if ( checked )
384  {
386  }
387 
388  checked = checkLineExtension( geom.vertexAt( snap.vertexIndex() + 1 ) );
389  if ( checked )
390  {
392  }
393  }
394  }
395 
396  // *****************************
397  // ---- Distance constraint
398  if ( ctx.distanceConstraint.locked && ctx.cadPoints().count() >= 2 )
399  {
400  if ( ctx.xConstraint.locked || ctx.yConstraint.locked
401  || !std::isnan( res.softLockX ) || !std::isnan( res.softLockY ) )
402  {
403  // perform both to detect errors in constraints
404  if ( ctx.xConstraint.locked || !std::isnan( res.softLockX ) )
405  {
406  const QgsPointXY verticalPt0( point.x(), point.y() );
407  const QgsPointXY verticalPt1( point.x(), point.y() + 1 );
408  const bool intersect = QgsGeometryUtils::lineCircleIntersection( previousPt, ctx.distanceConstraint.value, verticalPt0, verticalPt1, point );
409 
410  if ( ctx.xConstraint.locked )
411  {
412  res.valid &= intersect;
413  }
414  else if ( !intersect )
415  {
416  res.softLockX = std::numeric_limits<double>::quiet_NaN();
417  res.softLockY = std::numeric_limits<double>::quiet_NaN(); // in the case of the 2 soft locks are activated
418  res.valid &= QgsGeometryUtils::lineCircleIntersection( previousPt, ctx.distanceConstraint.value, previousPt, point, point );
419  }
420  }
421  if ( ctx.yConstraint.locked || !std::isnan( res.softLockY ) )
422  {
423  const QgsPointXY horizontalPt0( point.x(), point.y() );
424  const QgsPointXY horizontalPt1( point.x() + 1, point.y() );
425  const bool intersect = QgsGeometryUtils::lineCircleIntersection( previousPt, ctx.distanceConstraint.value, horizontalPt0, horizontalPt1, point );
426 
427  if ( ctx.yConstraint.locked )
428  {
429  res.valid &= intersect;
430  }
431  else if ( !intersect )
432  {
433  res.softLockY = std::numeric_limits<double>::quiet_NaN();
434  res.valid &= QgsGeometryUtils::lineCircleIntersection( previousPt, ctx.distanceConstraint.value, previousPt, point, point );
435  }
436  }
437  }
439  {
440  const QgsPointLocator::Match snap = ctx.lockedSnapVertices().last();
441  const QgsFeature feature = snap.layer()->getFeature( snap.featureId() );
442  const QgsGeometry geom = feature.geometry();
443 
444 
445  const QgsPointXY lineExtensionPt1 = snap.point();
446 
447  QgsPointXY lineExtensionPt2;
449  {
450  lineExtensionPt2 = QgsPointXY( geom.vertexAt( snap.vertexIndex() + 1 ) );
451  }
452  else
453  {
454  lineExtensionPt2 = QgsPointXY( geom.vertexAt( snap.vertexIndex() - 1 ) );
455  }
456 
457  const bool intersect = QgsGeometryUtils::lineCircleIntersection( previousPt, ctx.distanceConstraint.value, lineExtensionPt1, lineExtensionPt2, point );
458  if ( !intersect )
459  {
461  res.valid &= QgsGeometryUtils::lineCircleIntersection( previousPt, ctx.distanceConstraint.value, previousPt, point, point );
462  }
463  }
464  else
465  {
466  const double dist = std::sqrt( point.sqrDist( previousPt ) );
467  if ( dist == 0 )
468  {
469  // handle case where mouse is over origin and distance constraint is enabled
470  // take arbitrary horizontal line
471  point.set( previousPt.x() + ctx.distanceConstraint.value, previousPt.y() );
472  }
473  else
474  {
475  const double vP = ctx.distanceConstraint.value / dist;
476  point.set( previousPt.x() + ( point.x() - previousPt.x() ) * vP,
477  previousPt.y() + ( point.y() - previousPt.y() ) * vP );
478  }
479 
480  if ( snapMatch.hasEdge() && !ctx.angleConstraint.locked )
481  {
482  // we will magnietize to the intersection of that segment and the lockedDistance !
483  res.valid &= QgsGeometryUtils::lineCircleIntersection( previousPt, ctx.distanceConstraint.value, edgePt0, edgePt1, point );
484  }
485  }
486  }
487 
488  // *****************************
489  // ---- calculate CAD values
490  QgsDebugMsgLevel( QStringLiteral( "point: %1 %2" ).arg( point.x() ).arg( point.y() ), 4 );
491  QgsDebugMsgLevel( QStringLiteral( "previous point: %1 %2" ).arg( previousPt.x() ).arg( previousPt.y() ), 4 );
492  QgsDebugMsgLevel( QStringLiteral( "penultimate point: %1 %2" ).arg( penultimatePt.x() ).arg( penultimatePt.y() ), 4 );
493  //QgsDebugMsg( QStringLiteral( "dx: %1 dy: %2" ).arg( point.x() - previousPt.x() ).arg( point.y() - previousPt.y() ) );
494  //QgsDebugMsg( QStringLiteral( "ddx: %1 ddy: %2" ).arg( previousPt.x() - penultimatePt.x() ).arg( previousPt.y() - penultimatePt.y() ) );
495 
496  res.finalMapPoint = point;
497 
498  return res;
499 }
500 
502 {
503  QgsDebugMsg( QStringLiteral( "Constraints (locked / relative / value" ) );
504  QgsDebugMsg( QStringLiteral( "Angle: %1 %2 %3" ).arg( angleConstraint.locked ).arg( angleConstraint.relative ).arg( angleConstraint.value ) );
505  QgsDebugMsg( QStringLiteral( "Distance: %1 %2 %3" ).arg( distanceConstraint.locked ).arg( distanceConstraint.relative ).arg( distanceConstraint.value ) );
506  QgsDebugMsg( QStringLiteral( "X: %1 %2 %3" ).arg( xConstraint.locked ).arg( xConstraint.relative ).arg( xConstraint.value ) );
507  QgsDebugMsg( QStringLiteral( "Y: %1 %2 %3" ).arg( yConstraint.locked ).arg( yConstraint.relative ).arg( yConstraint.value ) );
508 }
@ AfterVertex
Lock to next vertex.
@ NoVertex
Don't lock to vertex.
@ BeforeVertex
Lock to previous vertex.
bool locked
Whether the constraint is active, i.e. should be considered.
Definition: qgscadutils.h:57
double value
Numeric value of the constraint (coordinate/distance in map units or angle in degrees)
Definition: qgscadutils.h:61
bool relative
Whether the value is relative to previous value.
Definition: qgscadutils.h:59
Defines constraints for the QgsCadUtils::alignMapPoint() method.
Definition: qgscadutils.h:104
QgsCadUtils::AlignMapPointConstraint xyVertexConstraint
Definition: qgscadutils.h:135
QgsCadUtils::AlignMapPointConstraint yConstraint
Constraint for Y coordinate.
Definition: qgscadutils.h:114
QgsCadUtils::AlignMapPointConstraint xConstraint
Constraint for X coordinate.
Definition: qgscadutils.h:112
double mapUnitsPerPixel
Map units/pixel ratio from map canvas.
Definition: qgscadutils.h:109
QgsPoint cadPoint(int index) const
Returns the recent CAD point at the specified index (in map coordinates).
Definition: qgscadutils.h:176
QgsCadUtils::AlignMapPointConstraint distanceConstraint
Constraint for distance.
Definition: qgscadutils.h:128
QgsSnappingUtils * snappingUtils
Snapping utils that will be used to snap point to map. Must not be nullptr.
Definition: qgscadutils.h:107
QgsCadUtils::AlignMapPointConstraint commonAngleConstraint
Constraint for soft lock to a common angle.
Definition: qgscadutils.h:132
QQueue< QgsPointLocator::Match > lockedSnapVertices() const
Returns the queue of point locator matches that contain the locked vertices.
Definition: qgscadutils.h:194
QgsCadUtils::AlignMapPointConstraint lineExtensionConstraint
Definition: qgscadutils.h:134
QList< QgsPoint > cadPoints() const
Returns the list of recent CAD points in map coordinates.
Definition: qgscadutils.h:152
void dump() const
Dumps the context's properties, for debugging.
QgsCadUtils::AlignMapPointConstraint angleConstraint
Constraint for angle.
Definition: qgscadutils.h:130
Structure returned from alignMapPoint() method.
Definition: qgscadutils.h:70
Qgis::LineExtensionSide softLockLineExtension
Definition: qgscadutils.h:93
QgsPointXY finalMapPoint
map point aligned according to the constraints
Definition: qgscadutils.h:76
bool valid
Whether the combination of constraints is actually valid.
Definition: qgscadutils.h:73
QgsPointLocator::Match snapMatch
Snapped point - only valid if actually used for something.
Definition: qgscadutils.h:82
QgsPointLocator::Match edgeMatch
Snapped segment - only valid if actually used for something.
Definition: qgscadutils.h:88
double softLockCommonAngle
Angle (in degrees) to which we have soft-locked ourselves (if not set it is -1)
Definition: qgscadutils.h:91
static QgsCadUtils::AlignMapPointOutput alignMapPoint(const QgsPointXY &originalMapPoint, const QgsCadUtils::AlignMapPointContext &ctx)
Applies X/Y/angle/distance constraints from the given context to a map point.
Definition: qgscadutils.cpp:39
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
static bool lineIntersection(const QgsPoint &p1, QgsVector v1, const QgsPoint &p2, QgsVector v2, QgsPoint &intersection) SIP_HOLDGIL
Computes the intersection between two lines.
static bool lineCircleIntersection(const QgsPointXY &center, double radius, const QgsPointXY &linePoint1, const QgsPointXY &linePoint2, QgsPointXY &intersection) SIP_HOLDGIL
Compute the intersection of a line and a circle.
static double distToInfiniteLine(const QgsPoint &point, const QgsPoint &linePoint1, const QgsPoint &linePoint2, double epsilon=1e-7)
Returns the distance between a point and an infinite line.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
A class to represent a 2D point.
Definition: qgspointxy.h:59
bool isEmpty() const SIP_HOLDGIL
Returns true if the geometry is empty.
Definition: qgspointxy.h:249
void set(double x, double y) SIP_HOLDGIL
Sets the x and y value of the point.
Definition: qgspointxy.h:139
double sqrDist(double x, double y) const SIP_HOLDGIL
Returns the squared distance between this point a specified x, y coordinate.
Definition: qgspointxy.h:190
void setX(double x) SIP_HOLDGIL
Sets the x value of the point.
Definition: qgspointxy.h:122
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
void setY(double y) SIP_HOLDGIL
Sets the y value of the point.
Definition: qgspointxy.h:132
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
QgsPointLocator::Match snapToMap(QPoint point, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Snap to map according to the current configuration.
QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
A class to represent a vector.
Definition: qgsvector.h:30
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2062
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
Interface that allows rejection of some matches in intersection queries (e.g.
virtual bool acceptMatch(const QgsPointLocator::Match &match)=0
QgsVectorLayer * layer() const
The vector layer where the snap occurred.
QgsFeatureId featureId() const
The id of the feature to which the snapped geometry belongs.
QgsPointXY point() const
for vertex / edge match coords depending on what class returns it (geom.cache: layer coords,...
bool hasEdge() const
Returns true if the Match is an edge.
void edgePoints(QgsPointXY &pt1, QgsPointXY &pt2) const
Only for a valid edge match - obtain endpoints of the edge.
int vertexIndex() const
for vertex / edge match (first vertex of the edge)