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 Q_FOREACH (
const QgsSnapIndex::Cell &cell, mCells )
195 QgsSnapIndex::Cell &QgsSnapIndex::GridRow::getCreateCell(
int col )
197 if ( col < mColStartIdx )
199 for (
int i = col; i < mColStartIdx; ++i )
201 mCells.prepend( Cell() );
204 return mCells.front();
206 else if ( col >= mColStartIdx + mCells.size() )
208 for (
int i = mColStartIdx + mCells.size(); i <= col; ++i )
210 mCells.append( Cell() );
212 return mCells.back();
216 return mCells[col - mColStartIdx];
220 const QgsSnapIndex::Cell *QgsSnapIndex::GridRow::getCell(
int col )
const 222 if ( col < mColStartIdx || col >= mColStartIdx + mCells.size() )
228 return &mCells[col - mColStartIdx];
232 QList<QgsSnapIndex::SnapItem *> QgsSnapIndex::GridRow::getSnapItems(
int colStart,
int colEnd )
const 234 colStart = std::max( colStart, mColStartIdx );
235 colEnd = std::min( colEnd, mColStartIdx + mCells.size() - 1 );
237 QList<SnapItem *> items;
239 for (
int col = colStart; col <= colEnd; ++col )
241 items.append( mCells[col - mColStartIdx] );
248 QgsSnapIndex::QgsSnapIndex(
const QgsPoint &origin,
double cellSize )
250 , mCellSize( cellSize )
255 QgsSnapIndex::~QgsSnapIndex()
257 qDeleteAll( mCoordIdxs );
261 const QgsSnapIndex::Cell *QgsSnapIndex::getCell(
int col,
int row )
const 263 if ( row < mRowsStartIdx || row >= mRowsStartIdx + mGridRows.size() )
269 return mGridRows[row - mRowsStartIdx].getCell( col );
273 QgsSnapIndex::Cell &QgsSnapIndex::getCreateCell(
int col,
int row )
275 if ( row < mRowsStartIdx )
277 for (
int i = row; i < mRowsStartIdx; ++i )
279 mGridRows.prepend( GridRow() );
282 return mGridRows.front().getCreateCell( col );
284 else if ( row >= mRowsStartIdx + mGridRows.size() )
286 for (
int i = mRowsStartIdx + mGridRows.size(); i <= row; ++i )
288 mGridRows.append( GridRow() );
290 return mGridRows.back().getCreateCell( col );
294 return mGridRows[row - mRowsStartIdx].getCreateCell( col );
298 void QgsSnapIndex::addPoint(
const CoordIdx *idx,
bool isEndPoint )
301 int col = std::floor( ( p.
x() - mOrigin.x() ) / mCellSize );
302 int row = std::floor( ( p.
y() - mOrigin.y() ) / mCellSize );
303 getCreateCell( col, row ).append(
new PointSnapItem( idx, isEndPoint ) );
306 void QgsSnapIndex::addSegment(
const CoordIdx *idxFrom,
const CoordIdx *idxTo )
311 float x0 = ( pFrom.
x() - mOrigin.x() ) / mCellSize;
312 float y0 = ( pFrom.
y() - mOrigin.y() ) / mCellSize;
313 float x1 = ( pTo.
x() - mOrigin.x() ) / mCellSize;
314 float y1 = ( pTo.
y() - mOrigin.y() ) / mCellSize;
316 Raytracer rt( x0, y0, x1, y1 );
317 for ( ; rt.isValid(); rt.next() )
319 getCreateCell( rt.curCol(), rt.curRow() ).append(
new SegmentSnapItem( idxFrom, idxTo ) );
325 for (
int iPart = 0, nParts = geom->
partCount(); iPart < nParts; ++iPart )
327 for (
int iRing = 0, nRings = geom->
ringCount( iPart ); iRing < nRings; ++iRing )
331 if ( qgsgeometry_cast< const QgsSurface * >( geom ) )
333 else if (
const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( geom ) )
335 if ( curve->isClosed() )
339 for (
int iVert = 0; iVert < nVerts; ++iVert )
341 CoordIdx *idx =
new CoordIdx( geom,
QgsVertexId( iPart, iRing, iVert ) );
342 CoordIdx *idx1 =
new CoordIdx( geom,
QgsVertexId( iPart, iRing, iVert + 1 ) );
343 mCoordIdxs.append( idx );
344 mCoordIdxs.append( idx1 );
345 addPoint( idx, iVert == 0 || iVert == nVerts - 1 );
346 if ( iVert < nVerts - 1 )
347 addSegment( idx, idx1 );
361 float x0 = ( p.
x() - mOrigin.x() ) / mCellSize;
362 float y0 = ( p.
y() - mOrigin.y() ) / mCellSize;
363 float x1 = ( p2.
x() - mOrigin.x() ) / mCellSize;
364 float y1 = ( p2.
y() - mOrigin.y() ) / mCellSize;
366 Raytracer rt( x0, y0, x1, y1 );
367 double dMin = std::numeric_limits<double>::max();
369 for ( ; rt.isValid(); rt.next() )
371 const Cell *cell = getCell( rt.curCol(), rt.curRow() );
376 Q_FOREACH (
const SnapItem *item, *cell )
378 if ( item->type == SnapSegment )
381 if ( static_cast<const SegmentSnapItem *>( item )->getIntersection( p, p2, inter ) )
397 QgsSnapIndex::SnapItem *QgsSnapIndex::getSnapItem(
const QgsPoint &pos,
double tol, QgsSnapIndex::PointSnapItem **pSnapPoint, QgsSnapIndex::SegmentSnapItem **pSnapSegment,
bool endPointOnly )
const 399 int colStart = std::floor( ( pos.
x() - tol - mOrigin.x() ) / mCellSize );
400 int rowStart = std::floor( ( pos.
y() - tol - mOrigin.y() ) / mCellSize );
401 int colEnd = std::floor( ( pos.
x() + tol - mOrigin.x() ) / mCellSize );
402 int rowEnd = std::floor( ( pos.
y() + tol - mOrigin.y() ) / mCellSize );
404 rowStart = std::max( rowStart, mRowsStartIdx );
405 rowEnd = std::min( rowEnd, mRowsStartIdx + mGridRows.size() - 1 );
407 QList<SnapItem *> items;
408 for (
int row = rowStart; row <= rowEnd; ++row )
410 items.append( mGridRows[row - mRowsStartIdx].getSnapItems( colStart, colEnd ) );
413 double minDistSegment = std::numeric_limits<double>::max();
414 double minDistPoint = std::numeric_limits<double>::max();
415 QgsSnapIndex::SegmentSnapItem *snapSegment =
nullptr;
416 QgsSnapIndex::PointSnapItem *snapPoint =
nullptr;
418 Q_FOREACH ( QgsSnapIndex::SnapItem *item, items )
420 if ( ( ! endPointOnly && item->type == SnapPoint ) || item->type == SnapEndPoint )
423 if ( dist < minDistPoint )
426 snapPoint =
static_cast<PointSnapItem *
>( item );
429 else if ( item->type == SnapSegment && !endPointOnly )
432 if ( !static_cast<SegmentSnapItem *>( item )->getProjection( pos, pProj ) )
437 if ( dist < minDistSegment )
439 minDistSegment = dist;
440 snapSegment =
static_cast<SegmentSnapItem *
>( item );
444 snapPoint = minDistPoint < tol * tol ? snapPoint :
nullptr;
445 snapSegment = minDistSegment < tol * tol ? snapSegment :
nullptr;
446 if ( pSnapPoint ) *pSnapPoint = snapPoint;
447 if ( pSnapSegment ) *pSnapSegment = snapSegment;
448 return minDistPoint < minDistSegment ? static_cast<QgsSnapIndex::SnapItem *>( snapPoint ) : static_cast<QgsSnapIndex::SnapItem *>( snapSegment );
459 : mReferenceSource( referenceSource )
468 QtConcurrent::blockingMap( list, ProcessFeatureWrapper(
this, snapTolerance, mode ) );
472 void QgsGeometrySnapper::processFeature(
QgsFeature &feature,
double snapTolerance,
SnapMode mode )
482 QList<QgsGeometry> refGeometries;
485 searchBounds.
grow( snapTolerance );
487 mIndexMutex.unlock();
490 mReferenceLayerMutex.lock();
496 refGeometries.append( refFeature.
geometry() );
498 mReferenceLayerMutex.unlock();
500 return snapGeometry( geometry, snapTolerance, refGeometries, mode );
512 QgsSnapIndex refSnapIndex( center, 10 * snapTolerance );
513 Q_FOREACH (
const QgsGeometry &geom, referenceGeometries )
515 refSnapIndex.addGeometry( geom.
constGet() );
520 QList < QList< QList<PointFlag> > > subjPointFlags;
523 for (
int iPart = 0, nParts = subjGeom->
partCount(); iPart < nParts; ++iPart )
525 subjPointFlags.append( QList< QList<PointFlag> >() );
527 for (
int iRing = 0, nRings = subjGeom->
ringCount( iPart ); iRing < nRings; ++iRing )
529 subjPointFlags[iPart].append( QList<PointFlag>() );
531 for (
int iVert = 0, nVerts = polyLineSize( subjGeom, iPart, iRing ); iVert < nVerts; ++iVert )
537 subjPointFlags[iPart][iRing].append( Unsnapped );
541 QgsSnapIndex::PointSnapItem *snapPoint =
nullptr;
542 QgsSnapIndex::SegmentSnapItem *snapSegment =
nullptr;
545 if ( !refSnapIndex.getSnapItem( p, snapTolerance, &snapPoint, &snapSegment, mode ==
EndPointToEndPoint ) )
547 subjPointFlags[iPart][iRing].append( Unsnapped );
561 subjGeom->
moveVertex( vidx, snapPoint->getSnapPoint( p ) );
562 subjPointFlags[iPart][iRing].append( SnappedToRefNode );
564 else if ( snapSegment )
566 subjGeom->
moveVertex( vidx, snapSegment->getSnapPoint( p ) );
567 subjPointFlags[iPart][iRing].append( SnappedToRefSegment );
577 double distanceNode = std::numeric_limits<double>::max();
578 double distanceSegment = std::numeric_limits<double>::max();
581 nodeSnap = snapPoint->getSnapPoint( p );
586 segmentSnap = snapSegment->getSnapPoint( p );
589 if ( snapPoint && distanceNode < distanceSegment )
592 subjPointFlags[iPart][iRing].append( SnappedToRefNode );
594 else if ( snapSegment )
597 subjPointFlags[iPart][iRing].append( SnappedToRefSegment );
608 if ( qgsgeometry_cast< const QgsPoint * >( subjGeom ) )
619 std::unique_ptr< QgsSnapIndex > subjSnapIndex(
new QgsSnapIndex( center, 10 * snapTolerance ) );
620 subjSnapIndex->addGeometry( subjGeom );
622 std::unique_ptr< QgsAbstractGeometry > origSubjGeom( subjGeom->
clone() );
623 std::unique_ptr< QgsSnapIndex > origSubjSnapIndex(
new QgsSnapIndex( center, 10 * snapTolerance ) );
624 origSubjSnapIndex->addGeometry( origSubjGeom.get() );
627 Q_FOREACH (
const QgsGeometry &refGeom, referenceGeometries )
629 for (
int iPart = 0, nParts = refGeom.
constGet()->
partCount(); iPart < nParts; ++iPart )
631 for (
int iRing = 0, nRings = refGeom.
constGet()->
ringCount( iPart ); iRing < nRings; ++iRing )
633 for (
int iVert = 0, nVerts = polyLineSize( refGeom.
constGet(), iPart, iRing ); iVert < nVerts; ++iVert )
636 QgsSnapIndex::PointSnapItem *snapPoint =
nullptr;
637 QgsSnapIndex::SegmentSnapItem *snapSegment =
nullptr;
639 if ( subjSnapIndex->getSnapItem( point, snapTolerance, &snapPoint, &snapSegment ) )
646 else if ( snapSegment )
649 QgsPoint pProj = snapSegment->getSnapPoint( point );
650 QgsPoint closest = refSnapIndex.getClosestSnapToPoint( point, pProj );
657 if ( !origSubjSnapIndex->getSnapItem( point, snapTolerance ) )
662 const QgsSnapIndex::CoordIdx *idx = snapSegment->idxFrom;
664 subjPointFlags[idx->vidx.part][idx->vidx.ring].insert( idx->vidx.vertex + 1, SnappedToRefNode );
665 subjSnapIndex.reset(
new QgsSnapIndex( center, 10 * snapTolerance ) );
666 subjSnapIndex->addGeometry( subjGeom );
673 subjSnapIndex.reset();
674 origSubjSnapIndex.reset();
675 origSubjGeom.reset();
678 for (
int iPart = 0, nParts = subjGeom->
partCount(); iPart < nParts; ++iPart )
680 for (
int iRing = 0, nRings = subjGeom->
ringCount( iPart ); iRing < nRings; ++iRing )
683 for (
int iVert = 0, nVerts = polyLineSize( subjGeom, iPart, iRing ); iVert < nVerts; ++iVert )
685 int iPrev = ( iVert - 1 + nVerts ) % nVerts;
686 int iNext = ( iVert + 1 ) % nVerts;
691 if ( subjPointFlags[iPart][iRing][iVert] == SnappedToRefSegment &&
692 subjPointFlags[iPart][iRing][iPrev] != Unsnapped &&
693 subjPointFlags[iPart][iRing][iNext] != Unsnapped &&
696 if ( ( ringIsClosed && nVerts > 3 ) || ( !ringIsClosed && nVerts > 2 ) )
699 subjPointFlags[iPart][iRing].removeAt( iVert );
718 int QgsGeometrySnapper::polyLineSize(
const QgsAbstractGeometry *geom,
int iPart,
int iRing )
722 if ( qgsgeometry_cast< const QgsSurface * >( geom ) || qgsgeometry_cast< const QgsMultiSurface * >( geom ) )
742 : mSnapTolerance( snapTolerance )
753 if ( !mFirstFeature )
758 searchBounds.
grow( mSnapTolerance );
760 if ( !refFeatureIds.isEmpty() )
762 QList< QgsGeometry > refGeometries;
765 refGeometries << mProcessedGeometries.value(
id );
771 mProcessedGeometries.insert( feat.
id(), geometry );
773 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.
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.