QGIS API Documentation 3.35.0-Master (f6e073f0eed)
Loading...
Searching...
No Matches
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
27static const double SOFT_CONSTRAINT_TOLERANCE_PIXEL = 15;
28static const double SOFT_CONSTRAINT_TOLERANCE_DEGREES = 10;
29
30
32struct 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 // Skip common angle constraint if the snapping to features has priority
167 && ( ! snapMatch.isValid() || ! ctx.snappingToFeaturesOverridesCommonAngle )
168 )
169 {
170 const double commonAngle = ctx.commonAngleConstraint.value * M_PI / 180;
171 // see if soft common angle constraint should be performed
172 // only if not in HardLock mode
173 double softAngle = std::atan2( point.y() - previousPt.y(),
174 point.x() - previousPt.x() );
175 double deltaAngle = 0;
176 if ( ctx.commonAngleConstraint.relative && ctx.cadPoints().count() >= 3 )
177 {
178 // compute the angle relative to the last segment (0° is aligned with last segment)
179 deltaAngle = std::atan2( previousPt.y() - penultimatePt.y(),
180 previousPt.x() - penultimatePt.x() );
181 softAngle -= deltaAngle;
182 }
183 const int quo = std::round( softAngle / commonAngle );
184 if ( std::fabs( softAngle - quo * commonAngle ) * 180.0 * M_1_PI <= SOFT_CONSTRAINT_TOLERANCE_DEGREES )
185 {
186 // also check the distance in pixel to the line, otherwise it's too sticky at long ranges
187 softAngle = quo * commonAngle;
188 // http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html
189 // use the direction vector (cos(a),sin(a)) from previous point. |x2-x1|=1 since sin2+cos2=1
190 const double dist = std::fabs( std::cos( softAngle + deltaAngle ) * ( previousPt.y() - point.y() )
191 - std::sin( softAngle + deltaAngle ) * ( previousPt.x() - point.x() ) );
192 if ( dist / ctx.mapUnitsPerPixel < SOFT_CONSTRAINT_TOLERANCE_PIXEL )
193 {
194 res.softLockCommonAngle = 180.0 / M_PI * softAngle;
195 }
196 }
197 }
198
199 // angle can be locked in one of the two ways:
200 // 1. "hard" lock defined by the user
201 // 2. "soft" lock from common angle (e.g. 45 degrees)
202 bool angleLocked = false, angleRelative = false;
203 double angleValueDeg = 0;
204 if ( ctx.angleConstraint.locked )
205 {
206 angleLocked = true;
207 angleRelative = ctx.angleConstraint.relative;
208 angleValueDeg = ctx.angleConstraint.value;
209 }
210 else if ( res.softLockCommonAngle != -1 )
211 {
212 angleLocked = true;
213 angleRelative = ctx.commonAngleConstraint.relative;
214 angleValueDeg = res.softLockCommonAngle;
215 }
216
217 // *****************************
218 // ---- Angle constraint
219 // input angles are in degrees
220 if ( angleLocked )
221 {
222 double angleValue = angleValueDeg * M_PI / 180;
223 if ( angleRelative && ctx.cadPoints().count() >= 3 )
224 {
225 // compute the angle relative to the last segment (0° is aligned with last segment)
226 angleValue += std::atan2( previousPt.y() - penultimatePt.y(),
227 previousPt.x() - penultimatePt.x() );
228 }
229
230 const double cosa = std::cos( angleValue );
231 const double sina = std::sin( angleValue );
232 const double v = ( point.x() - previousPt.x() ) * cosa + ( point.y() - previousPt.y() ) * sina;
233 if ( ctx.xConstraint.locked && ctx.yConstraint.locked )
234 {
235 // do nothing if both X,Y are already locked
236 }
237 else if ( ctx.xConstraint.locked || !std::isnan( res.softLockX ) )
238 {
239 if ( qgsDoubleNear( cosa, 0.0 ) )
240 {
241 res.valid = false;
242 }
243 else
244 {
245 double x = ctx.xConstraint.value;
246 if ( !ctx.xConstraint.relative )
247 {
248 x -= previousPt.x();
249 }
250 point.setY( previousPt.y() + x * sina / cosa );
251 }
252 }
253 else if ( ctx.yConstraint.locked || !std::isnan( res.softLockY ) )
254 {
255 if ( qgsDoubleNear( sina, 0.0 ) )
256 {
257 res.valid = false;
258 }
259 else
260 {
261 double y = ctx.yConstraint.value;
262 if ( !ctx.yConstraint.relative )
263 {
264 y -= previousPt.y();
265 }
266 point.setX( previousPt.x() + y * cosa / sina );
267 }
268 }
269 else
270 {
271 point.setX( previousPt.x() + cosa * v );
272 point.setY( previousPt.y() + sina * v );
273 }
274
275 if ( snapMatch.hasEdge() && !ctx.distanceConstraint.locked )
276 {
277 // magnetize to the intersection of the snapped segment and the lockedAngle
278
279 // line of previous point + locked angle
280 const double x1 = previousPt.x();
281 const double y1 = previousPt.y();
282 const double x2 = previousPt.x() + cosa;
283 const double y2 = previousPt.y() + sina;
284 // line of snapped segment
285 const double x3 = edgePt0.x();
286 const double y3 = edgePt0.y();
287 const double x4 = edgePt1.x();
288 const double y4 = edgePt1.y();
289
290 const double d = ( x1 - x2 ) * ( y3 - y4 ) - ( y1 - y2 ) * ( x3 - x4 );
291
292 // do not compute intersection if lines are almost parallel
293 // this threshold might be adapted
294 if ( std::fabs( d ) > 0.01 )
295 {
296 point.setX( ( ( x3 - x4 ) * ( x1 * y2 - y1 * x2 ) - ( x1 - x2 ) * ( x3 * y4 - y3 * x4 ) ) / d );
297 point.setY( ( ( y3 - y4 ) * ( x1 * y2 - y1 * x2 ) - ( y1 - y2 ) * ( x3 * y4 - y3 * x4 ) ) / d );
298 }
299 }
300 }
301
302 // *****************************
303 // ---- Line Extension Constraint
304
305 if ( numberOfHardLock < 2 && ctx.lineExtensionConstraint.locked && ctx.lockedSnapVertices().length() != 0 )
306 {
307 const QgsPointLocator::Match snap = ctx.lockedSnapVertices().last();
308 const QgsPointXY extensionPoint = snap.point();
309
310 if ( snap.layer() && !extensionPoint.isEmpty() )
311 {
312 auto checkLineExtension = [&]( QgsPoint vertex )
313 {
314 if ( vertex.isEmpty() )
315 {
316 return false;
317 }
318
319 const double distance = QgsGeometryUtils::distToInfiniteLine(
320 QgsPoint( point ), QgsPoint( extensionPoint ), vertex );
321
322 if ( distance / ctx.mapUnitsPerPixel < SOFT_CONSTRAINT_TOLERANCE_PIXEL )
323 {
324 if ( ctx.xConstraint.locked || !std::isnan( res.softLockX ) )
325 {
326 QgsPoint intersection;
327 const bool intersect = QgsGeometryUtils::lineIntersection(
328 QgsPoint( point ), QgsVector( 0, 1 ),
329 QgsPoint( extensionPoint ), QgsPoint( extensionPoint ) - vertex,
330 intersection
331 );
332 if ( intersect )
333 {
334 point = QgsPointXY( intersection );
335 }
336 }
337 else if ( ctx.yConstraint.locked || !std::isnan( res.softLockY ) )
338 {
339 QgsPoint intersection;
340 const bool intersect = QgsGeometryUtils::lineIntersection(
341 QgsPoint( point ), QgsVector( 1, 0 ),
342 QgsPoint( extensionPoint ), QgsPoint( extensionPoint ) - vertex,
343 intersection
344 );
345 if ( intersect )
346 {
347 point = QgsPointXY( intersection );
348 }
349 }
350 else if ( angleLocked )
351 {
352 const double angleValue = angleValueDeg * M_PI / 180;
353 const double cosa = std::cos( angleValue );
354 const double sina = std::sin( angleValue );
355
356 QgsPoint intersection;
358 QgsPoint( previousPt ), QgsVector( cosa, sina ),
359 QgsPoint( extensionPoint ), QgsPoint( extensionPoint ) - vertex,
360 intersection
361 );
362 point = QgsPointXY( intersection );
363 }
364 else
365 {
366 double angleValue = std::atan2( extensionPoint.y() - vertex.y(),
367 extensionPoint.x() - vertex.x() );
368
369 const double cosa = std::cos( angleValue );
370 const double sina = std::sin( angleValue );
371 const double v = ( point.x() - extensionPoint.x() ) * cosa + ( point.y() - extensionPoint.y() ) * sina;
372
373 point.setX( extensionPoint.x() + cosa * v );
374 point.setY( extensionPoint.y() + sina * v );
375 }
376
377 return true;
378 }
379 return false;
380 };
381
382 const QgsFeature feature = snap.layer()->getFeature( snap.featureId() );
383 const QgsGeometry geom = feature.geometry();
384
385 bool checked = checkLineExtension( geom.vertexAt( snap.vertexIndex() - 1 ) );
386 if ( checked )
387 {
389 }
390
391 checked = checkLineExtension( geom.vertexAt( snap.vertexIndex() + 1 ) );
392 if ( checked )
393 {
395 }
396 }
397 }
398
399 // *****************************
400 // ---- Distance constraint
401 if ( ctx.distanceConstraint.locked && ctx.cadPoints().count() >= 2 )
402 {
403 if ( ctx.xConstraint.locked || ctx.yConstraint.locked
404 || !std::isnan( res.softLockX ) || !std::isnan( res.softLockY ) )
405 {
406 // perform both to detect errors in constraints
407 if ( ctx.xConstraint.locked || !std::isnan( res.softLockX ) )
408 {
409 const QgsPointXY verticalPt0( point.x(), point.y() );
410 const QgsPointXY verticalPt1( point.x(), point.y() + 1 );
411 const bool intersect = QgsGeometryUtils::lineCircleIntersection( previousPt, ctx.distanceConstraint.value, verticalPt0, verticalPt1, point );
412
413 if ( ctx.xConstraint.locked )
414 {
415 res.valid &= intersect;
416 }
417 else if ( !intersect )
418 {
419 res.softLockX = std::numeric_limits<double>::quiet_NaN();
420 res.softLockY = std::numeric_limits<double>::quiet_NaN(); // in the case of the 2 soft locks are activated
421 res.valid &= QgsGeometryUtils::lineCircleIntersection( previousPt, ctx.distanceConstraint.value, previousPt, point, point );
422 }
423 }
424 if ( ctx.yConstraint.locked || !std::isnan( res.softLockY ) )
425 {
426 const QgsPointXY horizontalPt0( point.x(), point.y() );
427 const QgsPointXY horizontalPt1( point.x() + 1, point.y() );
428 const bool intersect = QgsGeometryUtils::lineCircleIntersection( previousPt, ctx.distanceConstraint.value, horizontalPt0, horizontalPt1, point );
429
430 if ( ctx.yConstraint.locked )
431 {
432 res.valid &= intersect;
433 }
434 else if ( !intersect )
435 {
436 res.softLockY = std::numeric_limits<double>::quiet_NaN();
437 res.valid &= QgsGeometryUtils::lineCircleIntersection( previousPt, ctx.distanceConstraint.value, previousPt, point, point );
438 }
439 }
440 }
442 {
443 const QgsPointLocator::Match snap = ctx.lockedSnapVertices().last();
444 const QgsFeature feature = snap.layer()->getFeature( snap.featureId() );
445 const QgsGeometry geom = feature.geometry();
446
447
448 const QgsPointXY lineExtensionPt1 = snap.point();
449
450 QgsPointXY lineExtensionPt2;
452 {
453 lineExtensionPt2 = QgsPointXY( geom.vertexAt( snap.vertexIndex() + 1 ) );
454 }
455 else
456 {
457 lineExtensionPt2 = QgsPointXY( geom.vertexAt( snap.vertexIndex() - 1 ) );
458 }
459
460 const bool intersect = QgsGeometryUtils::lineCircleIntersection( previousPt, ctx.distanceConstraint.value, lineExtensionPt1, lineExtensionPt2, point );
461 if ( !intersect )
462 {
464 res.valid &= QgsGeometryUtils::lineCircleIntersection( previousPt, ctx.distanceConstraint.value, previousPt, point, point );
465 }
466 }
467 else
468 {
469 const double dist = std::sqrt( point.sqrDist( previousPt ) );
470 if ( dist == 0 )
471 {
472 // handle case where mouse is over origin and distance constraint is enabled
473 // take arbitrary horizontal line
474 point.set( previousPt.x() + ctx.distanceConstraint.value, previousPt.y() );
475 }
476 else
477 {
478 const double vP = ctx.distanceConstraint.value / dist;
479 point.set( previousPt.x() + ( point.x() - previousPt.x() ) * vP,
480 previousPt.y() + ( point.y() - previousPt.y() ) * vP );
481 }
482
483 if ( snapMatch.hasEdge() && !ctx.angleConstraint.locked )
484 {
485 // we will magnietize to the intersection of that segment and the lockedDistance !
486 res.valid &= QgsGeometryUtils::lineCircleIntersection( previousPt, ctx.distanceConstraint.value, edgePt0, edgePt1, point );
487 }
488 }
489 }
490
491 // *****************************
492 // ---- calculate CAD values
493 QgsDebugMsgLevel( QStringLiteral( "point: %1 %2" ).arg( point.x() ).arg( point.y() ), 4 );
494 QgsDebugMsgLevel( QStringLiteral( "previous point: %1 %2" ).arg( previousPt.x() ).arg( previousPt.y() ), 4 );
495 QgsDebugMsgLevel( QStringLiteral( "penultimate point: %1 %2" ).arg( penultimatePt.x() ).arg( penultimatePt.y() ), 4 );
496 //QgsDebugMsgLevel( QStringLiteral( "dx: %1 dy: %2" ).arg( point.x() - previousPt.x() ).arg( point.y() - previousPt.y() ), 4 );
497 //QgsDebugMsgLevel( QStringLiteral( "ddx: %1 ddy: %2" ).arg( previousPt.x() - penultimatePt.x() ).arg( previousPt.y() - penultimatePt.y() ), 4 );
498
499 res.finalMapPoint = point;
500
501 return res;
502}
503
505{
506 QgsDebugMsgLevel( QStringLiteral( "Constraints (locked / relative / value" ), 1 );
507 QgsDebugMsgLevel( QStringLiteral( "Angle: %1 %2 %3" ).arg( angleConstraint.locked ).arg( angleConstraint.relative ).arg( angleConstraint.value ), 1 );
508 QgsDebugMsgLevel( QStringLiteral( "Distance: %1 %2 %3" ).arg( distanceConstraint.locked ).arg( distanceConstraint.relative ).arg( distanceConstraint.value ), 1 );
509 QgsDebugMsgLevel( QStringLiteral( "X: %1 %2 %3" ).arg( xConstraint.locked ).arg( xConstraint.relative ).arg( xConstraint.value ), 1 );
510 QgsDebugMsgLevel( QStringLiteral( "Y: %1 %2 %3" ).arg( yConstraint.locked ).arg( yConstraint.relative ).arg( yConstraint.value ), 1 );
511}
@ 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:55
double value
Numeric value of the constraint (coordinate/distance in map units or angle in degrees)
Definition qgscadutils.h:59
bool relative
Whether the value is relative to previous value.
Definition qgscadutils.h:57
Defines constraints for the QgsCadUtils::alignMapPoint() method.
QgsCadUtils::AlignMapPointConstraint xyVertexConstraint
QgsCadUtils::AlignMapPointConstraint yConstraint
Constraint for Y coordinate.
QgsCadUtils::AlignMapPointConstraint xConstraint
Constraint for X coordinate.
double mapUnitsPerPixel
Map units/pixel ratio from map canvas.
QgsPoint cadPoint(int index) const
Returns the recent CAD point at the specified index (in map coordinates).
QgsCadUtils::AlignMapPointConstraint distanceConstraint
Constraint for distance.
bool snappingToFeaturesOverridesCommonAngle
Flag to set snapping to features priority over common angle.
QgsSnappingUtils * snappingUtils
Snapping utils that will be used to snap point to map. Must not be nullptr.
QgsCadUtils::AlignMapPointConstraint commonAngleConstraint
Constraint for soft lock to a common angle.
QQueue< QgsPointLocator::Match > lockedSnapVertices() const
Returns the queue of point locator matches that contain the locked vertices.
QgsCadUtils::AlignMapPointConstraint lineExtensionConstraint
QList< QgsPoint > cadPoints() const
Returns the list of recent CAD points in map coordinates.
void dump() const
Dumps the context's properties, for debugging.
QgsCadUtils::AlignMapPointConstraint angleConstraint
Constraint for angle.
Structure returned from alignMapPoint() method.
Definition qgscadutils.h:67
Qgis::LineExtensionSide softLockLineExtension
Definition qgscadutils.h:90
QgsPointXY finalMapPoint
map point aligned according to the constraints
Definition qgscadutils.h:73
bool valid
Whether the combination of constraints is actually valid.
Definition qgscadutils.h:70
QgsPointLocator::Match snapMatch
Snapped point - only valid if actually used for something.
Definition qgscadutils.h:79
QgsPointLocator::Match edgeMatch
Snapped segment - only valid if actually used for something.
Definition qgscadutils.h:85
double softLockCommonAngle
Angle (in degrees) to which we have soft-locked ourselves (if not set it is -1)
Definition qgscadutils.h:88
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.
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)
Computes the intersection between two lines.
static bool lineCircleIntersection(const QgsPointXY &center, double radius, const QgsPointXY &linePoint1, const QgsPointXY &linePoint2, QgsPointXY &intersection)
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.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
A class to represent a 2D point.
Definition qgspointxy.h:60
double sqrDist(double x, double y) const
Returns the squared distance between this point a specified x, y coordinate.
Definition qgspointxy.h:187
void setY(double y)
Sets the y value of the point.
Definition qgspointxy.h:130
void set(double x, double y)
Sets the x and y value of the point.
Definition qgspointxy.h:137
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
void setX(double x)
Sets the x value of the point.
Definition qgspointxy.h:120
bool isEmpty() const
Returns true if the geometry is empty.
Definition qgspointxy.h:243
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:5144
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
Interface that allows rejection of some matches in intersection queries (e.g.
virtual bool acceptMatch(const QgsPointLocator::Match &match)=0
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,...
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)