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.