17 #include <QtConcurrentMap> 31 QgsSnapIndex::PointSnapItem::PointSnapItem(
const QgsSnapIndex::CoordIdx *_idx,
bool isEndPoint )
32 : SnapItem( isEndPoint ? QgsSnapIndex::SnapEndPoint : QgsSnapIndex::SnapPoint )
41 QgsSnapIndex::SegmentSnapItem::SegmentSnapItem(
const QgsSnapIndex::CoordIdx *_idxFrom,
const QgsSnapIndex::CoordIdx *_idxTo )
42 : SnapItem( QgsSnapIndex::SnapSegment )
47 QgsPoint QgsSnapIndex::SegmentSnapItem::getSnapPoint(
const QgsPoint &p )
const 54 const QgsPoint &q1 = idxFrom->point(), & q2 = idxTo->point();
58 double wl = w.length();
67 double d = v.y() * w.x() - v.x() * w.y();
72 double dx = q1.
x() - p1.
x();
73 double dy = q1.
y() - p1.
y();
74 double k = ( dy * w.x() - dx * w.y() ) / d;
76 inter =
QgsPoint( p1.
x() + v.x() * k, p1.
y() + v.y() * k );
78 double lambdav =
QgsVector( inter.
x() - p1.
x(), inter.
y() - p1.
y() ) * v;
79 if ( lambdav < 0. + 1E-8 || lambdav > vl - 1E-8 )
82 double lambdaw =
QgsVector( inter.
x() - q1.
x(), inter.
y() - q1.
y() ) * w;
83 return !( lambdaw < 0. + 1E-8 || lambdaw >= wl - 1E-8 );
86 bool QgsSnapIndex::SegmentSnapItem::getProjection(
const QgsPoint &p,
QgsPoint &pProj )
88 const QgsPoint &s1 = idxFrom->point();
90 double nx = s2.
y() - s1.
y();
91 double ny = -( s2.
x() - s1.
x() );
92 double t = ( p.
x() * ny - p.
y() * nx - s1.
x() * ny + s1.
y() * nx ) / ( ( s2.
x() - s1.
x() ) * ny - ( s2.
y() - s1.
y() ) * nx );
93 if ( t < 0. || t > 1. )
97 pProj =
QgsPoint( s1.
x() + ( s2.
x() - s1.
x() ) * t, s1.
y() + ( s2.
y() - s1.
y() ) * t );
108 Raytracer(
float x0,
float y0,
float x1,
float y1 )
109 : m_dx( std::fabs( x1 - x0 ) )
110 , m_dy( std::fabs( y1 - y0 ) )
111 , m_x( std::floor( x0 ) )
112 , m_y( std::floor( y0 ) )
118 m_error = std::numeric_limits<float>::infinity();
123 m_n += int( std::floor( x1 ) ) - m_x;
124 m_error = ( std::floor( x0 ) + 1 - x0 ) * m_dy;
129 m_n += m_x - int( std::floor( x1 ) );
130 m_error = ( x0 - std::floor( x0 ) ) * m_dy;
135 m_error = -std::numeric_limits<float>::infinity();
140 m_n += int( std::floor( y1 ) ) - m_y;
141 m_error -= ( std::floor( y0 ) + 1 - y0 ) * m_dx;
146 m_n += m_y - int( std::floor( y1 ) );
147 m_error -= ( y0 - std::floor( y0 ) ) * m_dx;
150 int curCol()
const {
return m_x; }
151 int curRow()
const {
return m_y; }
159 else if ( m_error < 0 )
175 bool isValid()
const {
return m_n > 0; }
187 QgsSnapIndex::GridRow::~GridRow()
189 const auto constMCells = mCells;
190 for (
const QgsSnapIndex::Cell &cell : constMCells )
196 QgsSnapIndex::Cell &QgsSnapIndex::GridRow::getCreateCell(
int col )
198 if ( col < mColStartIdx )
200 for (
int i = col; i < mColStartIdx; ++i )
202 mCells.prepend( Cell() );
205 return mCells.front();
207 else if ( col >= mColStartIdx + mCells.size() )
209 for (
int i = mColStartIdx + mCells.size(); i <= col; ++i )
211 mCells.append( Cell() );
213 return mCells.back();
217 return mCells[col - mColStartIdx];
221 const QgsSnapIndex::Cell *QgsSnapIndex::GridRow::getCell(
int col )
const 223 if ( col < mColStartIdx || col >= mColStartIdx + mCells.size() )
229 return &mCells[col - mColStartIdx];
233 QList<QgsSnapIndex::SnapItem *> QgsSnapIndex::GridRow::getSnapItems(
int colStart,
int colEnd )
const 235 colStart = std::max( colStart, mColStartIdx );
236 colEnd = std::min( colEnd, mColStartIdx + mCells.size() - 1 );
238 QList<SnapItem *> items;
240 for (
int col = colStart; col <= colEnd; ++col )
242 items.append( mCells[col - mColStartIdx] );
249 QgsSnapIndex::QgsSnapIndex(
const QgsPoint &origin,
double cellSize )
251 , mCellSize( cellSize )
256 QgsSnapIndex::~QgsSnapIndex()
258 qDeleteAll( mCoordIdxs );
262 const QgsSnapIndex::Cell *QgsSnapIndex::getCell(
int col,
int row )
const 264 if ( row < mRowsStartIdx || row >= mRowsStartIdx + mGridRows.size() )
270 return mGridRows[row - mRowsStartIdx].getCell( col );
274 QgsSnapIndex::Cell &QgsSnapIndex::getCreateCell(
int col,
int row )
276 if ( row < mRowsStartIdx )
278 for (
int i = row; i < mRowsStartIdx; ++i )
280 mGridRows.prepend( GridRow() );
283 return mGridRows.front().getCreateCell( col );
285 else if ( row >= mRowsStartIdx + mGridRows.size() )
287 for (
int i = mRowsStartIdx + mGridRows.size(); i <= row; ++i )
289 mGridRows.append( GridRow() );
291 return mGridRows.back().getCreateCell( col );
295 return mGridRows[row - mRowsStartIdx].getCreateCell( col );
299 void QgsSnapIndex::addPoint(
const CoordIdx *idx,
bool isEndPoint )
302 int col = std::floor( ( p.
x() - mOrigin.x() ) / mCellSize );
303 int row = std::floor( ( p.
y() - mOrigin.y() ) / mCellSize );
304 getCreateCell( col, row ).append(
new PointSnapItem( idx, isEndPoint ) );
307 void QgsSnapIndex::addSegment(
const CoordIdx *idxFrom,
const CoordIdx *idxTo )
312 float x0 = ( pFrom.
x() - mOrigin.x() ) / mCellSize;
313 float y0 = ( pFrom.
y() - mOrigin.y() ) / mCellSize;
314 float x1 = ( pTo.
x() - mOrigin.x() ) / mCellSize;
315 float y1 = ( pTo.
y() - mOrigin.y() ) / mCellSize;
317 Raytracer rt( x0, y0, x1, y1 );
318 for ( ; rt.isValid(); rt.next() )
320 getCreateCell( rt.curCol(), rt.curRow() ).append(
new SegmentSnapItem( idxFrom, idxTo ) );
326 for (
int iPart = 0, nParts = geom->
partCount(); iPart < nParts; ++iPart )
328 for (
int iRing = 0, nRings = geom->
ringCount( iPart ); iRing < nRings; ++iRing )
332 if ( qgsgeometry_cast< const QgsSurface * >( geom ) )
334 else if (
const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( geom ) )
336 if ( curve->isClosed() )
340 for (
int iVert = 0; iVert < nVerts; ++iVert )
342 CoordIdx *idx =
new CoordIdx( geom,
QgsVertexId( iPart, iRing, iVert ) );
343 CoordIdx *idx1 =
new CoordIdx( geom,
QgsVertexId( iPart, iRing, iVert + 1 ) );
344 mCoordIdxs.append( idx );
345 mCoordIdxs.append( idx1 );
346 addPoint( idx, iVert == 0 || iVert == nVerts - 1 );
347 if ( iVert < nVerts - 1 )
348 addSegment( idx, idx1 );
362 float x0 = ( p.
x() - mOrigin.x() ) / mCellSize;
363 float y0 = ( p.
y() - mOrigin.y() ) / mCellSize;
364 float x1 = ( p2.
x() - mOrigin.x() ) / mCellSize;
365 float y1 = ( p2.
y() - mOrigin.y() ) / mCellSize;
367 Raytracer rt( x0, y0, x1, y1 );
368 double dMin = std::numeric_limits<double>::max();
370 for ( ; rt.isValid(); rt.next() )
372 const Cell *cell = getCell( rt.curCol(), rt.curRow() );
377 for (
const SnapItem *item : *cell )
379 if ( item->type == SnapSegment )
382 if ( static_cast<const SegmentSnapItem *>( item )->getIntersection( p, p2, inter ) )
398 QgsSnapIndex::SnapItem *QgsSnapIndex::getSnapItem(
const QgsPoint &pos,
double tol, QgsSnapIndex::PointSnapItem **pSnapPoint, QgsSnapIndex::SegmentSnapItem **pSnapSegment,
bool endPointOnly )
const 400 int colStart = std::floor( ( pos.
x() - tol - mOrigin.x() ) / mCellSize );
401 int rowStart = std::floor( ( pos.
y() - tol - mOrigin.y() ) / mCellSize );
402 int colEnd = std::floor( ( pos.
x() + tol - mOrigin.x() ) / mCellSize );
403 int rowEnd = std::floor( ( pos.
y() + tol - mOrigin.y() ) / mCellSize );
405 rowStart = std::max( rowStart, mRowsStartIdx );
406 rowEnd = std::min( rowEnd, mRowsStartIdx + mGridRows.size() - 1 );
408 QList<SnapItem *> items;
409 for (
int row = rowStart; row <= rowEnd; ++row )
411 items.append( mGridRows[row - mRowsStartIdx].getSnapItems( colStart, colEnd ) );
414 double minDistSegment = std::numeric_limits<double>::max();
415 double minDistPoint = std::numeric_limits<double>::max();
416 QgsSnapIndex::SegmentSnapItem *snapSegment =
nullptr;
417 QgsSnapIndex::PointSnapItem *snapPoint =
nullptr;
419 const auto constItems = items;
420 for ( QgsSnapIndex::SnapItem *item : constItems )
422 if ( ( ! endPointOnly && item->type == SnapPoint ) || item->type == SnapEndPoint )
425 if ( dist < minDistPoint )
428 snapPoint =
static_cast<PointSnapItem *
>( item );
431 else if ( item->type == SnapSegment && !endPointOnly )
434 if ( !static_cast<SegmentSnapItem *>( item )->getProjection( pos, pProj ) )
439 if ( dist < minDistSegment )
441 minDistSegment = dist;
442 snapSegment =
static_cast<SegmentSnapItem *
>( item );
446 snapPoint = minDistPoint < tol * tol ? snapPoint :
nullptr;
447 snapSegment = minDistSegment < tol * tol ? snapSegment :
nullptr;
448 if ( pSnapPoint ) *pSnapPoint = snapPoint;
449 if ( pSnapSegment ) *pSnapSegment = snapSegment;
450 return minDistPoint < minDistSegment ? static_cast<QgsSnapIndex::SnapItem *>( snapPoint ) : static_cast<QgsSnapIndex::SnapItem *>( snapSegment );
461 : mReferenceSource( referenceSource )
470 QtConcurrent::blockingMap( list, ProcessFeatureWrapper(
this, snapTolerance, mode ) );
474 void QgsGeometrySnapper::processFeature(
QgsFeature &feature,
double snapTolerance,
SnapMode mode )
484 QList<QgsGeometry> refGeometries;
487 searchBounds.
grow( snapTolerance );
489 mIndexMutex.unlock();
492 mReferenceLayerMutex.lock();
498 refGeometries.append( refFeature.
geometry() );
500 mReferenceLayerMutex.unlock();
502 return snapGeometry( geometry, snapTolerance, refGeometries, mode );
514 QgsSnapIndex refSnapIndex( center, 10 * snapTolerance );
515 for (
const QgsGeometry &geom : referenceGeometries )
517 refSnapIndex.addGeometry( geom.constGet() );
522 QList < QList< QList<PointFlag> > > subjPointFlags;
525 for (
int iPart = 0, nParts = subjGeom->
partCount(); iPart < nParts; ++iPart )
527 subjPointFlags.append( QList< QList<PointFlag> >() );
529 for (
int iRing = 0, nRings = subjGeom->
ringCount( iPart ); iRing < nRings; ++iRing )
531 subjPointFlags[iPart].append( QList<PointFlag>() );
533 for (
int iVert = 0, nVerts = polyLineSize( subjGeom, iPart, iRing ); iVert < nVerts; ++iVert )
539 subjPointFlags[iPart][iRing].append( Unsnapped );
543 QgsSnapIndex::PointSnapItem *snapPoint =
nullptr;
544 QgsSnapIndex::SegmentSnapItem *snapSegment =
nullptr;
547 if ( !refSnapIndex.getSnapItem( p, snapTolerance, &snapPoint, &snapSegment, mode ==
EndPointToEndPoint ) )
549 subjPointFlags[iPart][iRing].append( Unsnapped );
563 subjGeom->
moveVertex( vidx, snapPoint->getSnapPoint( p ) );
564 subjPointFlags[iPart][iRing].append( SnappedToRefNode );
566 else if ( snapSegment )
568 subjGeom->
moveVertex( vidx, snapSegment->getSnapPoint( p ) );
569 subjPointFlags[iPart][iRing].append( SnappedToRefSegment );
579 double distanceNode = std::numeric_limits<double>::max();
580 double distanceSegment = std::numeric_limits<double>::max();
583 nodeSnap = snapPoint->getSnapPoint( p );
588 segmentSnap = snapSegment->getSnapPoint( p );
591 if ( snapPoint && distanceNode < distanceSegment )
594 subjPointFlags[iPart][iRing].append( SnappedToRefNode );
596 else if ( snapSegment )
599 subjPointFlags[iPart][iRing].append( SnappedToRefSegment );
610 if ( qgsgeometry_cast< const QgsPoint * >( subjGeom ) )
621 std::unique_ptr< QgsSnapIndex > subjSnapIndex(
new QgsSnapIndex( center, 10 * snapTolerance ) );
622 subjSnapIndex->addGeometry( subjGeom );
624 std::unique_ptr< QgsAbstractGeometry > origSubjGeom( subjGeom->
clone() );
625 std::unique_ptr< QgsSnapIndex > origSubjSnapIndex(
new QgsSnapIndex( center, 10 * snapTolerance ) );
626 origSubjSnapIndex->addGeometry( origSubjGeom.get() );
629 for (
const QgsGeometry &refGeom : referenceGeometries )
631 for (
int iPart = 0, nParts = refGeom.constGet()->partCount(); iPart < nParts; ++iPart )
633 for (
int iRing = 0, nRings = refGeom.constGet()->ringCount( iPart ); iRing < nRings; ++iRing )
635 for (
int iVert = 0, nVerts = polyLineSize( refGeom.constGet(), iPart, iRing ); iVert < nVerts; ++iVert )
638 QgsSnapIndex::PointSnapItem *snapPoint =
nullptr;
639 QgsSnapIndex::SegmentSnapItem *snapSegment =
nullptr;
641 if ( subjSnapIndex->getSnapItem( point, snapTolerance, &snapPoint, &snapSegment ) )
648 else if ( snapSegment )
651 QgsPoint pProj = snapSegment->getSnapPoint( point );
652 QgsPoint closest = refSnapIndex.getClosestSnapToPoint( point, pProj );
659 if ( !origSubjSnapIndex->getSnapItem( point, snapTolerance ) )
664 const QgsSnapIndex::CoordIdx *idx = snapSegment->idxFrom;
666 subjPointFlags[idx->vidx.part][idx->vidx.ring].insert( idx->vidx.vertex + 1, SnappedToRefNode );
667 subjSnapIndex.reset(
new QgsSnapIndex( center, 10 * snapTolerance ) );
668 subjSnapIndex->addGeometry( subjGeom );
675 subjSnapIndex.reset();
676 origSubjSnapIndex.reset();
677 origSubjGeom.reset();
680 for (
int iPart = 0, nParts = subjGeom->
partCount(); iPart < nParts; ++iPart )
682 for (
int iRing = 0, nRings = subjGeom->
ringCount( iPart ); iRing < nRings; ++iRing )
685 for (
int iVert = 0, nVerts = polyLineSize( subjGeom, iPart, iRing ); iVert < nVerts; ++iVert )
687 int iPrev = ( iVert - 1 + nVerts ) % nVerts;
688 int iNext = ( iVert + 1 ) % nVerts;
693 if ( subjPointFlags[iPart][iRing][iVert] == SnappedToRefSegment &&
694 subjPointFlags[iPart][iRing][iPrev] != Unsnapped &&
695 subjPointFlags[iPart][iRing][iNext] != Unsnapped &&
698 if ( ( ringIsClosed && nVerts > 3 ) || ( !ringIsClosed && nVerts > 2 ) )
701 subjPointFlags[iPart][iRing].removeAt( iVert );
720 int QgsGeometrySnapper::polyLineSize(
const QgsAbstractGeometry *geom,
int iPart,
int iRing )
724 if ( qgsgeometry_cast< const QgsSurface * >( geom ) || qgsgeometry_cast< const QgsMultiSurface * >( geom ) )
744 : mSnapTolerance( snapTolerance )
755 if ( !mFirstFeature )
760 searchBounds.
grow( mSnapTolerance );
762 if ( !refFeatureIds.isEmpty() )
764 QList< QgsGeometry > refGeometries;
765 const auto constRefFeatureIds = refFeatureIds;
768 refGeometries << mProcessedGeometries.value(
id );
774 mProcessedGeometries.insert( feat.
id(), geometry );
776 mFirstFeature =
false;
Wrapper for iterator of features from vector data provider or vector layer.
A rectangle specified with double values.
QSet< QgsFeatureId > QgsFeatureIds
virtual bool deleteVertex(QgsVertexId position)=0
Deletes a vertex within the geometry.
bool removeDuplicateNodes(double epsilon=4 *std::numeric_limits< double >::epsilon(), bool useZValues=false)
Removes duplicate nodes from the geometry, wherever removing the nodes does not result in a degenerat...
virtual bool insertVertex(QgsVertexId position, const QgsPoint &vertex)=0
Inserts a vertex into the geometry.
QList< QgsFeature > QgsFeatureList
QgsWkbTypes::Type wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Only snap the start/end points of lines to other start/end points of lines.
QgsGeometrySnapper(QgsFeatureSource *referenceSource)
Constructor for QgsGeometrySnapper.
void featureSnapped()
Emitted each time a feature has been processed when calling snapFeatures()
A geometry is the spatial representation of a feature.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
virtual QgsRectangle boundingBox() const =0
Returns the minimal bounding box for the geometry.
bool hasGeometry() const
Returns true if the feature has an associated geometry.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
QgsPoint vertexAt(QgsVertexId) const override
Returns the point corresponding to a specified vertex id.
QgsFeatureList snapFeatures(const QgsFeatureList &features, double snapTolerance, SnapMode mode=PreferNodes)
Snaps a set of features to the reference layer and returns the result.
Utility class for identifying a unique vertex within a geometry.
void grow(double delta)
Grows the rectangle in place by the specified amount.
Only snap start/end points of lines (point features will also be snapped, polygon features will not b...
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
static GeometryType geometryType(Type type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
This class wraps a request for features to a vector layer (or directly its vector data provider)...
T qgsgeometry_cast(const QgsAbstractGeometry *geom)
virtual int ringCount(int part=0) const =0
Returns the number of rings of which this geometry is built.
Abstract base class for curved geometry type.
Abstract base class for all geometries.
QgsWkbTypes::Type wkbType() const
Returns the WKB type of the geometry.
Point geometry type, with support for z-dimension and m-values.
double length() const
Returns the length of the vector.
A class to represent a vector.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsGeometry snapGeometry(const QgsGeometry &geometry, double snapTolerance, SnapMode mode=PreferNodes) const
Snaps a geometry to the reference layer and returns the result.
Prefer to snap to nodes, even when a segment may be closer than a node. New nodes will be inserted to...
static double sqrDistance2D(const QgsPoint &pt1, const QgsPoint &pt2)
Returns the squared 2D distance between two points.
A spatial index for QgsFeature objects.
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets feature IDs that should be fetched.
Snap to closest point, regardless of it is a node or a segment. New nodes will be inserted to make ge...
An interface for objects which provide features via a getFeatures method.
virtual bool moveVertex(QgsVertexId position, const QgsPoint &newPos)=0
Moves a vertex within the geometry.
QgsGeometry snapFeature(const QgsFeature &feature)
Snaps a single feature's geometry against all feature geometries already processed by calls to snapFe...
virtual int vertexCount(int part=0, int ring=0) const =0
Returns the number of vertices of which this geometry is built.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
static QgsPoint projectPointOnSegment(const QgsPoint &p, const QgsPoint &s1, const QgsPoint &s2)
Project the point on a segment.
QgsInternalGeometrySnapper(double snapTolerance, QgsGeometrySnapper::SnapMode mode=QgsGeometrySnapper::PreferNodes)
Constructor for QgsInternalGeometrySnapper.
Prefer to snap to nodes, even when a segment may be closer than a node. No new nodes will be inserted...
QgsPointXY center() const
Returns the center point of the rectangle.
Only snap start/end points of lines (point features will also be snapped, polygon features will not b...
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=nullptr) override
Adds a feature to the index.
bool nextFeature(QgsFeature &f)
double distanceSquared(double x, double y) const
Returns the squared distance between this point a specified x, y coordinate.
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
Snap to closest point, regardless of it is a node or a segment. No new nodes will be inserted...
QList< QgsFeatureId > intersects(const QgsRectangle &rectangle) const
Returns a list of features with a bounding box which intersects the specified rectangle.
virtual int partCount() const =0
Returns count of parts contained in the geometry.
virtual QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const =0
Returns an iterator for the features in the source.