29 #include <QtConcurrentMap>
34 QgsSnapIndex::PointSnapItem::PointSnapItem(
const QgsSnapIndex::CoordIdx *_idx,
bool isEndPoint )
35 : SnapItem( isEndPoint ? QgsSnapIndex::SnapEndPoint : QgsSnapIndex::SnapPoint )
44 QgsSnapIndex::SegmentSnapItem::SegmentSnapItem(
const QgsSnapIndex::CoordIdx *_idxFrom,
const QgsSnapIndex::CoordIdx *_idxTo )
45 : SnapItem( QgsSnapIndex::SnapSegment )
50 QgsPoint QgsSnapIndex::SegmentSnapItem::getSnapPoint(
const QgsPoint &p )
const
57 const QgsPoint &q1 = idxFrom->point(), & q2 = idxTo->point();
60 const double vl = v.length();
61 const double wl = w.length();
70 const double d = v.y() * w.x() - v.x() * w.y();
75 const double dx = q1.
x() - p1.
x();
76 const double dy = q1.
y() - p1.
y();
77 const double k = ( dy * w.x() - dx * w.y() ) / d;
79 inter =
QgsPoint( p1.
x() + v.x() * k, p1.
y() + v.y() * k );
81 const double lambdav =
QgsVector( inter.
x() - p1.
x(), inter.
y() - p1.
y() ) * v;
82 if ( lambdav < 0. + 1E-8 || lambdav > vl - 1E-8 )
85 const double lambdaw =
QgsVector( inter.
x() - q1.
x(), inter.
y() - q1.
y() ) * w;
86 return !( lambdaw < 0. + 1E-8 || lambdaw >= wl - 1E-8 );
89 bool QgsSnapIndex::SegmentSnapItem::getProjection(
const QgsPoint &p,
QgsPoint &pProj )
const
91 const QgsPoint &s1 = idxFrom->point();
93 const double nx = s2.
y() - s1.
y();
94 const double ny = -( s2.
x() - s1.
x() );
95 const double t = ( p.
x() * ny - p.
y() * nx - s1.
x() * ny + s1.
y() * nx ) / ( ( s2.
x() - s1.
x() ) * ny - ( s2.
y() - s1.
y() ) * nx );
96 if ( t < 0. || t > 1. )
100 pProj =
QgsPoint( s1.
x() + ( s2.
x() - s1.
x() ) * t, s1.
y() + ( s2.
y() - s1.
y() ) * t );
104 bool QgsSnapIndex::SegmentSnapItem::withinDistance(
const QgsPoint &p,
const double tolerance )
106 double minDistX, minDistY;
107 const double distance =
QgsGeometryUtils::sqrDistToLine( p.
x(), p.
y(), idxFrom->point().x(), idxFrom->point().y(), idxTo->point().x(), idxTo->point().y(), minDistX, minDistY, 4 * std::numeric_limits<double>::epsilon() );
108 return distance <= tolerance;
113 QgsSnapIndex::QgsSnapIndex()
118 QgsSnapIndex::~QgsSnapIndex()
120 qDeleteAll( mCoordIdxs );
121 qDeleteAll( mSnapItems );
126 void QgsSnapIndex::addPoint(
const CoordIdx *idx,
bool isEndPoint )
131 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
134 GEOSCoordSequence *seq = GEOSCoordSeq_create_r( geosctxt, 1, 2 );
135 GEOSCoordSeq_setX_r( geosctxt, seq, 0, p.
x() );
136 GEOSCoordSeq_setY_r( geosctxt, seq, 0, p.
y() );
140 PointSnapItem *item =
new PointSnapItem( idx, isEndPoint );
141 GEOSSTRtree_insert_r( geosctxt, mSTRTree, point.get(), item );
142 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR<9
143 mSTRTreeItems.emplace_back( std::move( point ) );
148 void QgsSnapIndex::addSegment(
const CoordIdx *idxFrom,
const CoordIdx *idxTo )
150 const QgsPoint pointFrom = idxFrom->point();
151 const QgsPoint pointTo = idxTo->point();
155 GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 2, 2 );
156 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
157 GEOSCoordSeq_setXY_r( geosctxt, coord, 0, pointFrom.
x(), pointFrom.
y() );
158 GEOSCoordSeq_setXY_r( geosctxt, coord, 1, pointTo.
x(), pointTo.
y() );
160 GEOSCoordSeq_setX_r( geosctxt, coord, 0, pointFrom.
x() );
161 GEOSCoordSeq_setY_r( geosctxt, coord, 0, pointFrom.
y() );
162 GEOSCoordSeq_setX_r( geosctxt, coord, 1, pointTo.
x() );
163 GEOSCoordSeq_setY_r( geosctxt, coord, 1, pointTo.
y() );
167 SegmentSnapItem *item =
new SegmentSnapItem( idxFrom, idxTo );
168 GEOSSTRtree_insert_r( geosctxt, mSTRTree,
segment.get(), item );
169 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR<9
170 mSTRTreeItems.push_back( std::move(
segment ) );
177 for (
int iPart = 0, nParts = geom->
partCount(); iPart < nParts; ++iPart )
179 for (
int iRing = 0, nRings = geom->
ringCount( iPart ); iRing < nRings; ++iRing )
183 if ( qgsgeometry_cast< const QgsSurface * >( geom ) )
185 else if (
const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( geom ) )
187 if ( curve->isClosed() )
191 for (
int iVert = 0; iVert < nVerts; ++iVert )
193 CoordIdx *idx =
new CoordIdx( geom,
QgsVertexId( iPart, iRing, iVert ) );
194 CoordIdx *idx1 =
new CoordIdx( geom,
QgsVertexId( iPart, iRing, iVert + 1 ) );
195 mCoordIdxs.append( idx );
196 mCoordIdxs.append( idx1 );
197 addPoint( idx, iVert == 0 || iVert == nVerts - 1 );
198 if ( iVert < nVerts - 1 )
199 addSegment( idx, idx1 );
207 QList< QgsSnapIndex::SnapItem * > *
list;
212 reinterpret_cast<_GEOSQueryCallbackData *
>( userdata )->list->append(
static_cast<QgsSnapIndex::SnapItem *
>( item ) );
221 const QgsPoint endPoint( 2 * midPoint.
x() - startPoint.
x(), 2 * midPoint.
y() - startPoint.
y() );
224 double minDistance = std::numeric_limits<double>::max();
226 GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 2, 2 );
227 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
228 GEOSCoordSeq_setXY_r( geosctxt, coord, 0, startPoint.
x(), startPoint.
y() );
229 GEOSCoordSeq_setXY_r( geosctxt, coord, 1, endPoint.x(), endPoint.y() );
231 GEOSCoordSeq_setX_r( geosctxt, coord, 0, startPoint.
x() );
232 GEOSCoordSeq_setY_r( geosctxt, coord, 0, startPoint.
y() );
233 GEOSCoordSeq_setX_r( geosctxt, coord, 1, endPoint.x() );
234 GEOSCoordSeq_setY_r( geosctxt, coord, 1, endPoint.y() );
236 geos::unique_ptr searchDiagonal( GEOSGeom_createLineString_r( geosctxt, coord ) );
238 QList<SnapItem *> items;
240 callbackData.
list = &items;
241 GEOSSTRtree_query_r( geosctxt, mSTRTree, searchDiagonal.get(),
_GEOSQueryCallback, &callbackData );
242 for (
const SnapItem *item : std::as_const( items ) )
244 if ( item->type == SnapSegment )
247 if (
static_cast<const SegmentSnapItem *
>( item )->getIntersection( startPoint, endPoint, inter ) )
250 if ( dist < minDistance )
262 QgsSnapIndex::SnapItem *QgsSnapIndex::getSnapItem(
const QgsPoint &pos,
const double tolerance, QgsSnapIndex::PointSnapItem **pSnapPoint, QgsSnapIndex::SegmentSnapItem **pSnapSegment,
bool endPointOnly )
const
266 GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 2, 2 );
267 #if GEOS_VERSION_MAJOR>3 || GEOS_VERSION_MINOR>=8
268 GEOSCoordSeq_setXY_r( geosctxt, coord, 0, pos.
x() - tolerance, pos.
y() - tolerance );
269 GEOSCoordSeq_setXY_r( geosctxt, coord, 1, pos.
x() + tolerance, pos.
y() + tolerance );
271 GEOSCoordSeq_setX_r( geosctxt, coord, 0, pos.
x() - tolerance );
272 GEOSCoordSeq_setY_r( geosctxt, coord, 0, pos.
y() - tolerance );
273 GEOSCoordSeq_setX_r( geosctxt, coord, 1, pos.
x() + tolerance );
274 GEOSCoordSeq_setY_r( geosctxt, coord, 1, pos.
y() + tolerance );
277 geos::unique_ptr searchDiagonal( GEOSGeom_createLineString_r( geosctxt, coord ) );
279 QList<SnapItem *> items;
281 callbackData.
list = &items;
282 GEOSSTRtree_query_r( geosctxt, mSTRTree, searchDiagonal.get(),
_GEOSQueryCallback, &callbackData );
284 double minDistSegment = std::numeric_limits<double>::max();
285 double minDistPoint = std::numeric_limits<double>::max();
286 QgsSnapIndex::SegmentSnapItem *snapSegment =
nullptr;
287 QgsSnapIndex::PointSnapItem *snapPoint =
nullptr;
289 const auto constItems = items;
290 for ( QgsSnapIndex::SnapItem *item : constItems )
292 if ( ( ! endPointOnly && item->type == SnapPoint ) || item->type == SnapEndPoint )
295 if ( dist < minDistPoint )
298 snapPoint =
static_cast<PointSnapItem *
>( item );
301 else if ( item->type == SnapSegment && !endPointOnly )
303 if ( !
static_cast<SegmentSnapItem *
>( item )->withinDistance( pos, tolerance ) )
307 if ( !
static_cast<SegmentSnapItem *
>( item )->getProjection( pos, pProj ) )
311 if ( dist < minDistSegment )
313 minDistSegment = dist;
314 snapSegment =
static_cast<SegmentSnapItem *
>( item );
318 snapPoint = minDistPoint < tolerance * tolerance ? snapPoint :
nullptr;
319 snapSegment = minDistSegment < tolerance * tolerance ? snapSegment :
nullptr;
320 if ( pSnapPoint ) *pSnapPoint = snapPoint;
321 if ( pSnapSegment ) *pSnapSegment = snapSegment;
322 return minDistPoint < minDistSegment ? static_cast<QgsSnapIndex::SnapItem *>( snapPoint ) : static_cast<QgsSnapIndex::SnapItem *>( snapSegment );
333 : mReferenceSource( referenceSource )
342 QtConcurrent::blockingMap(
list, ProcessFeatureWrapper(
this, snapTolerance, mode ) );
346 void QgsGeometrySnapper::processFeature(
QgsFeature &feature,
double snapTolerance, SnapMode mode )
356 QList<QgsGeometry> refGeometries;
359 searchBounds.
grow( snapTolerance );
361 mIndexMutex.unlock();
363 if ( refFeatureIds.isEmpty() )
366 refGeometries.reserve( refFeatureIds.size() );
368 const QgsFeatureIds cachedIds = qgis::listToSet( mCachedReferenceGeometries.keys() );
371 if ( cachedIds.contains(
id ) )
373 refGeometries.append( mCachedReferenceGeometries[
id] );
377 missingFeatureIds << id;
381 if ( missingFeatureIds.size() > 0 )
384 mReferenceLayerMutex.lock();
390 refGeometries.append( refFeature.
geometry() );
392 mReferenceLayerMutex.unlock();
395 return snapGeometry( geometry, snapTolerance, refGeometries, mode );
407 QgsSnapIndex refSnapIndex;
408 for (
const QgsGeometry &geom : referenceGeometries )
410 refSnapIndex.addGeometry( geom.constGet() );
415 QList < QList< QList<PointFlag> > > subjPointFlags;
418 for (
int iPart = 0, nParts = subjGeom->
partCount(); iPart < nParts; ++iPart )
420 subjPointFlags.append( QList< QList<PointFlag> >() );
422 for (
int iRing = 0, nRings = subjGeom->
ringCount( iPart ); iRing < nRings; ++iRing )
424 subjPointFlags[iPart].append( QList<PointFlag>() );
426 for (
int iVert = 0, nVerts = polyLineSize( subjGeom, iPart, iRing ); iVert < nVerts; ++iVert )
432 subjPointFlags[iPart][iRing].append( Unsnapped );
436 QgsSnapIndex::PointSnapItem *snapPoint =
nullptr;
437 QgsSnapIndex::SegmentSnapItem *snapSegment =
nullptr;
440 if ( !refSnapIndex.getSnapItem( p, snapTolerance, &snapPoint, &snapSegment, mode ==
EndPointToEndPoint ) )
442 subjPointFlags[iPart][iRing].append( Unsnapped );
456 subjGeom->
moveVertex( vidx, snapPoint->getSnapPoint( p ) );
457 subjPointFlags[iPart][iRing].append( SnappedToRefNode );
459 else if ( snapSegment )
461 subjGeom->
moveVertex( vidx, snapSegment->getSnapPoint( p ) );
462 subjPointFlags[iPart][iRing].append( SnappedToRefSegment );
472 double distanceNode = std::numeric_limits<double>::max();
473 double distanceSegment = std::numeric_limits<double>::max();
476 nodeSnap = snapPoint->getSnapPoint( p );
481 segmentSnap = snapSegment->getSnapPoint( p );
484 if ( snapPoint && distanceNode < distanceSegment )
487 subjPointFlags[iPart][iRing].append( SnappedToRefNode );
489 else if ( snapSegment )
492 subjPointFlags[iPart][iRing].append( SnappedToRefSegment );
503 if ( qgsgeometry_cast< const QgsPoint * >( subjGeom ) )
514 std::unique_ptr< QgsSnapIndex > subjSnapIndex(
new QgsSnapIndex() );
515 subjSnapIndex->addGeometry( subjGeom );
517 std::unique_ptr< QgsAbstractGeometry > origSubjGeom( subjGeom->
clone() );
518 std::unique_ptr< QgsSnapIndex > origSubjSnapIndex(
new QgsSnapIndex() );
519 origSubjSnapIndex->addGeometry( origSubjGeom.get() );
522 for (
const QgsGeometry &refGeom : referenceGeometries )
524 for (
int iPart = 0, nParts = refGeom.constGet()->partCount(); iPart < nParts; ++iPart )
526 for (
int iRing = 0, nRings = refGeom.constGet()->ringCount( iPart ); iRing < nRings; ++iRing )
528 for (
int iVert = 0, nVerts = polyLineSize( refGeom.constGet(), iPart, iRing ); iVert < nVerts; ++iVert )
530 QgsSnapIndex::PointSnapItem *snapPoint =
nullptr;
531 QgsSnapIndex::SegmentSnapItem *snapSegment =
nullptr;
533 if ( subjSnapIndex->getSnapItem( point, snapTolerance, &snapPoint, &snapSegment ) )
538 const QgsPoint snappedPoint = snapPoint->getSnapPoint( point );
546 const QgsPoint pProj = snapSegment->getSnapPoint( point );
547 const QgsPoint closest = refSnapIndex.getClosestSnapToPoint( point, pProj );
554 if ( !origSubjSnapIndex->getSnapItem( point, snapTolerance ) )
559 const QgsSnapIndex::CoordIdx *idx = snapSegment->idxFrom;
561 subjPointFlags[idx->vidx.part][idx->vidx.ring].insert( idx->vidx.vertex + 1, SnappedToRefNode );
562 subjSnapIndex.reset(
new QgsSnapIndex() );
563 subjSnapIndex->addGeometry( subjGeom );
570 subjSnapIndex.reset();
571 origSubjSnapIndex.reset();
572 origSubjGeom.reset();
575 for (
int iPart = 0, nParts = subjGeom->
partCount(); iPart < nParts; ++iPart )
577 for (
int iRing = 0, nRings = subjGeom->
ringCount( iPart ); iRing < nRings; ++iRing )
580 for (
int iVert = 0, nVerts = polyLineSize( subjGeom, iPart, iRing ); iVert < nVerts; ++iVert )
582 const int iPrev = ( iVert - 1 + nVerts ) % nVerts;
583 const int iNext = ( iVert + 1 ) % nVerts;
588 if ( subjPointFlags[iPart][iRing][iVert] == SnappedToRefSegment &&
589 subjPointFlags[iPart][iRing][iPrev] != Unsnapped &&
590 subjPointFlags[iPart][iRing][iNext] != Unsnapped &&
593 if ( ( ringIsClosed && nVerts > 3 ) || ( !ringIsClosed && nVerts > 2 ) )
596 subjPointFlags[iPart][iRing].removeAt( iVert );
615 int QgsGeometrySnapper::polyLineSize(
const QgsAbstractGeometry *geom,
int iPart,
int iRing )
617 const int nVerts = geom->
vertexCount( iPart, iRing );
619 if ( qgsgeometry_cast< const QgsSurface * >( geom ) || qgsgeometry_cast< const QgsMultiSurface * >( geom ) )
639 : mSnapTolerance( snapTolerance )
650 if ( !mFirstFeature )
655 searchBounds.
grow( mSnapTolerance );
657 if ( !refFeatureIds.isEmpty() )
659 QList< QgsGeometry > refGeometries;
660 const auto constRefFeatureIds = refFeatureIds;
663 refGeometries << mProcessedGeometries.value(
id );
669 mProcessedGeometries.insert( feat.
id(), geometry );
671 mFirstFeature =
false;