21#include "moc_qgsgeometrysnapper.cpp" 
   28#include <QtConcurrentMap> 
   33QgsSnapIndex::PointSnapItem::PointSnapItem( 
const QgsSnapIndex::CoordIdx *_idx, 
bool isEndPoint )
 
   34  : SnapItem( isEndPoint ? QgsSnapIndex::SnapEndPoint : QgsSnapIndex::SnapPoint )
 
   43QgsSnapIndex::SegmentSnapItem::SegmentSnapItem( 
const QgsSnapIndex::CoordIdx *_idxFrom, 
const QgsSnapIndex::CoordIdx *_idxTo )
 
   44  : SnapItem( QgsSnapIndex::SnapSegment )
 
   49QgsPoint QgsSnapIndex::SegmentSnapItem::getSnapPoint( 
const QgsPoint &p )
 const 
   56  const QgsPoint &q1 = idxFrom->point(), &q2 = idxTo->point();
 
   59  const double vl = v.length();
 
   60  const double wl = w.length();
 
   69  const double d = v.y() * w.x() - v.x() * w.y();
 
   74  const double dx = q1.
x() - p1.
x();
 
   75  const double dy = q1.
y() - p1.
y();
 
   76  const double k = ( dy * w.x() - dx * w.y() ) / d;
 
   78  inter = 
QgsPoint( p1.
x() + v.x() * k, p1.
y() + v.y() * k );
 
   80  const double lambdav = 
QgsVector( inter.
x() - p1.
x(), inter.
y() - p1.
y() ) * v;
 
   81  if ( lambdav < 0. + 1E-8 || lambdav > vl - 1E-8 )
 
   84  const double lambdaw = 
QgsVector( inter.
x() - q1.
x(), inter.
y() - q1.
y() ) * w;
 
   85  return !( lambdaw < 0. + 1E-8 || lambdaw >= wl - 1E-8 );
 
   88bool QgsSnapIndex::SegmentSnapItem::getProjection( 
const QgsPoint &p, 
QgsPoint &pProj )
 const 
   90  const QgsPoint &s1 = idxFrom->point();
 
   92  const double nx = s2.
y() - s1.
y();
 
   93  const double ny = -( s2.
x() - s1.
x() );
 
   94  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 );
 
   95  if ( t < 0. || t > 1. )
 
   99  pProj = 
QgsPoint( s1.
x() + ( s2.
x() - s1.
x() ) * t, s1.
y() + ( s2.
y() - s1.
y() ) * t );
 
  103bool QgsSnapIndex::SegmentSnapItem::withinSquaredDistance( 
const QgsPoint &p, 
const double squaredDistance )
 
  105  double minDistX, minDistY;
 
  106  return QgsGeometryUtilsBase::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() ) <= squaredDistance;
 
  111QgsSnapIndex::QgsSnapIndex()
 
  116QgsSnapIndex::~QgsSnapIndex()
 
  118  qDeleteAll( mCoordIdxs );
 
  119  qDeleteAll( mSnapItems );
 
  124void QgsSnapIndex::addPoint( 
const CoordIdx *idx, 
bool isEndPoint )
 
  129  geos::unique_ptr point( GEOSGeom_createPointFromXY_r( geosctxt, p.
x(), p.
y() ) );
 
  131  PointSnapItem *item = 
new PointSnapItem( idx, isEndPoint );
 
  132  GEOSSTRtree_insert_r( geosctxt, mSTRTree, point.get(), item );
 
  136void QgsSnapIndex::addSegment( 
const CoordIdx *idxFrom, 
const CoordIdx *idxTo )
 
  138  const QgsPoint pointFrom = idxFrom->point();
 
  139  const QgsPoint pointTo = idxTo->point();
 
  143  GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 2, 2 );
 
  144  GEOSCoordSeq_setXY_r( geosctxt, coord, 0, pointFrom.
x(), pointFrom.
y() );
 
  145  GEOSCoordSeq_setXY_r( geosctxt, coord, 1, pointTo.
x(), pointTo.
y() );
 
  146  geos::unique_ptr 
segment( GEOSGeom_createLineString_r( geosctxt, coord ) );
 
  148  SegmentSnapItem *item = 
new SegmentSnapItem( idxFrom, idxTo );
 
  149  GEOSSTRtree_insert_r( geosctxt, mSTRTree, 
segment.get(), item );
 
  155  for ( 
int iPart = 0, nParts = geom->
partCount(); iPart < nParts; ++iPart )
 
  157    for ( 
int iRing = 0, nRings = geom->
ringCount( iPart ); iRing < nRings; ++iRing )
 
  161      if ( qgsgeometry_cast<const QgsSurface *>( geom ) )
 
  163      else if ( 
const QgsCurve *curve = qgsgeometry_cast<const QgsCurve *>( geom ) )
 
  165        if ( curve->isClosed() )
 
  169      for ( 
int iVert = 0; iVert < nVerts; ++iVert )
 
  171        CoordIdx *idx = 
new CoordIdx( geom, 
QgsVertexId( iPart, iRing, iVert ) );
 
  172        CoordIdx *idx1 = 
new CoordIdx( geom, 
QgsVertexId( iPart, iRing, iVert + 1 ) );
 
  173        mCoordIdxs.append( idx );
 
  174        mCoordIdxs.append( idx1 );
 
  175        addPoint( idx, iVert == 0 || iVert == nVerts - 1 );
 
  176        if ( iVert < nVerts - 1 )
 
  177          addSegment( idx, idx1 );
 
  185    QList<QgsSnapIndex::SnapItem *> *
list;
 
  190  reinterpret_cast<_GEOSQueryCallbackData *
>( userdata )->list->append( 
static_cast<QgsSnapIndex::SnapItem *
>( item ) );
 
  199  const QgsPoint endPoint( 2 * midPoint.
x() - startPoint.
x(), 2 * midPoint.
y() - startPoint.
y() );
 
  202  double minDistance = std::numeric_limits<double>::max();
 
  204  GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 2, 2 );
 
  205  GEOSCoordSeq_setXY_r( geosctxt, coord, 0, startPoint.
x(), startPoint.
y() );
 
  206  GEOSCoordSeq_setXY_r( geosctxt, coord, 1, endPoint.x(), endPoint.y() );
 
  207  geos::unique_ptr searchDiagonal( GEOSGeom_createLineString_r( geosctxt, coord ) );
 
  209  QList<SnapItem *> items;
 
  211  callbackData.
list = &items;
 
  212  GEOSSTRtree_query_r( geosctxt, mSTRTree, searchDiagonal.get(), 
_GEOSQueryCallback, &callbackData );
 
  213  for ( 
const SnapItem *item : std::as_const( items ) )
 
  215    if ( item->type == SnapSegment )
 
  218      if ( 
static_cast<const SegmentSnapItem *
>( item )->getIntersection( startPoint, endPoint, inter ) )
 
  221        if ( dist < minDistance )
 
  233QgsSnapIndex::SnapItem *QgsSnapIndex::getSnapItem( 
const QgsPoint &pos, 
const double tolerance, QgsSnapIndex::PointSnapItem **pSnapPoint, QgsSnapIndex::SegmentSnapItem **pSnapSegment, 
bool endPointOnly )
 const 
  237  GEOSCoordSequence *coord = GEOSCoordSeq_create_r( geosctxt, 2, 2 );
 
  238  GEOSCoordSeq_setXY_r( geosctxt, coord, 0, pos.
x() - tolerance, pos.
y() - tolerance );
 
  239  GEOSCoordSeq_setXY_r( geosctxt, coord, 1, pos.
x() + tolerance, pos.
y() + tolerance );
 
  241  geos::unique_ptr searchDiagonal( GEOSGeom_createLineString_r( geosctxt, coord ) );
 
  243  QList<SnapItem *> items;
 
  245  callbackData.
list = &items;
 
  246  GEOSSTRtree_query_r( geosctxt, mSTRTree, searchDiagonal.get(), 
_GEOSQueryCallback, &callbackData );
 
  248  double minDistSegment = std::numeric_limits<double>::max();
 
  249  double minDistPoint = std::numeric_limits<double>::max();
 
  250  QgsSnapIndex::SegmentSnapItem *snapSegment = 
nullptr;
 
  251  QgsSnapIndex::PointSnapItem *snapPoint = 
nullptr;
 
  253  const double squaredTolerance = tolerance * tolerance;
 
  254  const auto constItems = items;
 
  255  for ( QgsSnapIndex::SnapItem *item : constItems )
 
  257    if ( ( !endPointOnly && item->type == SnapPoint ) || item->type == SnapEndPoint )
 
  260      if ( dist < minDistPoint )
 
  263        snapPoint = 
static_cast<PointSnapItem *
>( item );
 
  266    else if ( item->type == SnapSegment && !endPointOnly )
 
  268      if ( !
static_cast<SegmentSnapItem *
>( item )->withinSquaredDistance( pos, squaredTolerance ) )
 
  272      if ( !
static_cast<SegmentSnapItem *
>( item )->getProjection( pos, pProj ) )
 
  276      if ( dist < minDistSegment )
 
  278        minDistSegment = dist;
 
  279        snapSegment = 
static_cast<SegmentSnapItem *
>( item );
 
  283  snapPoint = minDistPoint < squaredTolerance ? snapPoint : 
nullptr;
 
  284  snapSegment = minDistSegment < squaredTolerance ? snapSegment : 
nullptr;
 
  286    *pSnapPoint = snapPoint;
 
  288    *pSnapSegment = snapSegment;
 
  289  return minDistPoint < minDistSegment ? static_cast<QgsSnapIndex::SnapItem *>( snapPoint ) : static_cast<QgsSnapIndex::SnapItem *>( snapSegment );
 
  300  : mReferenceSource( referenceSource )
 
 
  309  QtConcurrent::blockingMap( list, ProcessFeatureWrapper( 
this, snapTolerance, mode ) );
 
 
  313void QgsGeometrySnapper::processFeature( 
QgsFeature &feature, 
double snapTolerance, SnapMode mode )
 
  323  QList<QgsGeometry> refGeometries;
 
  326  searchBounds.
grow( snapTolerance );
 
  328  mIndexMutex.unlock();
 
  330  if ( refFeatureIds.isEmpty() )
 
  333  refGeometries.reserve( refFeatureIds.size() );
 
  335  const QgsFeatureIds cachedIds = qgis::listToSet( mCachedReferenceGeometries.keys() );
 
  338    if ( cachedIds.contains( 
id ) )
 
  340      refGeometries.append( mCachedReferenceGeometries[
id] );
 
  344      missingFeatureIds << id;
 
  348  if ( missingFeatureIds.size() > 0 )
 
  350    mReferenceLayerMutex.lock();
 
  356      refGeometries.append( refFeature.
geometry() );
 
  358    mReferenceLayerMutex.unlock();
 
  361  return snapGeometry( geometry, snapTolerance, refGeometries, mode );
 
 
  371  QgsSnapIndex refSnapIndex;
 
  372  for ( 
const QgsGeometry &geom : referenceGeometries )
 
  374    refSnapIndex.addGeometry( geom.constGet() );
 
  379  QList<QList<QList<PointFlag>>> subjPointFlags;
 
  382  for ( 
int iPart = 0, nParts = subjGeom->
partCount(); iPart < nParts; ++iPart )
 
  384    subjPointFlags.append( QList<QList<PointFlag>>() );
 
  386    for ( 
int iRing = 0, nRings = subjGeom->
ringCount( iPart ); iRing < nRings; ++iRing )
 
  388      subjPointFlags[iPart].append( QList<PointFlag>() );
 
  390      for ( 
int iVert = 0, nVerts = polyLineSize( subjGeom, iPart, iRing ); iVert < nVerts; ++iVert )
 
  395          subjPointFlags[iPart][iRing].append( Unsnapped );
 
  399        QgsSnapIndex::PointSnapItem *snapPoint = 
nullptr;
 
  400        QgsSnapIndex::SegmentSnapItem *snapSegment = 
nullptr;
 
  403        if ( !refSnapIndex.getSnapItem( p, snapTolerance, &snapPoint, &snapSegment, mode == 
EndPointToEndPoint ) )
 
  405          subjPointFlags[iPart][iRing].append( Unsnapped );
 
  419                subjGeom->
moveVertex( vidx, snapPoint->getSnapPoint( p ) );
 
  420                subjPointFlags[iPart][iRing].append( SnappedToRefNode );
 
  422              else if ( snapSegment )
 
  424                subjGeom->
moveVertex( vidx, snapSegment->getSnapPoint( p ) );
 
  425                subjPointFlags[iPart][iRing].append( SnappedToRefSegment );
 
  435              double distanceNode = std::numeric_limits<double>::max();
 
  436              double distanceSegment = std::numeric_limits<double>::max();
 
  439                nodeSnap = snapPoint->getSnapPoint( p );
 
  444                segmentSnap = snapSegment->getSnapPoint( p );
 
  447              if ( snapPoint && distanceNode < distanceSegment )
 
  450                subjPointFlags[iPart][iRing].append( SnappedToRefNode );
 
  452              else if ( snapSegment )
 
  455                subjPointFlags[iPart][iRing].append( SnappedToRefSegment );
 
  466  if ( qgsgeometry_cast<const QgsPoint *>( subjGeom ) )
 
  477  auto subjSnapIndex = std::make_unique<QgsSnapIndex>();
 
  478  subjSnapIndex->addGeometry( subjGeom );
 
  480  std::unique_ptr<QgsAbstractGeometry> origSubjGeom( subjGeom->
clone() );
 
  481  auto origSubjSnapIndex = std::make_unique<QgsSnapIndex>();
 
  482  origSubjSnapIndex->addGeometry( origSubjGeom.get() );
 
  485  for ( 
const QgsGeometry &refGeom : referenceGeometries )
 
  487    for ( 
int iPart = 0, nParts = refGeom.constGet()->partCount(); iPart < nParts; ++iPart )
 
  489      for ( 
int iRing = 0, nRings = refGeom.constGet()->ringCount( iPart ); iRing < nRings; ++iRing )
 
  491        for ( 
int iVert = 0, nVerts = polyLineSize( refGeom.constGet(), iPart, iRing ); iVert < nVerts; ++iVert )
 
  493          QgsSnapIndex::PointSnapItem *snapPoint = 
nullptr;
 
  494          QgsSnapIndex::SegmentSnapItem *snapSegment = 
nullptr;
 
  496          if ( subjSnapIndex->getSnapItem( point, snapTolerance, &snapPoint, &snapSegment ) )
 
  501              const QgsPoint snappedPoint = snapPoint->getSnapPoint( point );
 
  509              const QgsPoint pProj = snapSegment->getSnapPoint( point );
 
  510              const QgsPoint closest = refSnapIndex.getClosestSnapToPoint( point, pProj );
 
  517              if ( !origSubjSnapIndex->getSnapItem( point, snapTolerance ) )
 
  522              const QgsSnapIndex::CoordIdx *idx = snapSegment->idxFrom;
 
  524              subjPointFlags[idx->vidx.part][idx->vidx.ring].insert( idx->vidx.vertex + 1, SnappedToRefNode );
 
  525              subjSnapIndex.reset( 
new QgsSnapIndex() );
 
  526              subjSnapIndex->addGeometry( subjGeom );
 
  533  subjSnapIndex.reset();
 
  534  origSubjSnapIndex.reset();
 
  535  origSubjGeom.reset();
 
  538  for ( 
int iPart = 0, nParts = subjGeom->
partCount(); iPart < nParts; ++iPart )
 
  540    for ( 
int iRing = 0, nRings = subjGeom->
ringCount( iPart ); iRing < nRings; ++iRing )
 
  543      for ( 
int iVert = 0, nVerts = polyLineSize( subjGeom, iPart, iRing ); iVert < nVerts; ++iVert )
 
  545        const int iPrev = ( iVert - 1 + nVerts ) % nVerts;
 
  546        const int iNext = ( iVert + 1 ) % nVerts;
 
  553          if ( ( ringIsClosed && nVerts > 3 ) || ( !ringIsClosed && nVerts > 2 ) )
 
  556            subjPointFlags[iPart][iRing].removeAt( iVert );
 
 
  575int QgsGeometrySnapper::polyLineSize( 
const QgsAbstractGeometry *geom, 
int iPart, 
int iRing )
 
  577  const int nVerts = geom->
vertexCount( iPart, iRing );
 
  579  if ( qgsgeometry_cast<const QgsSurface *>( geom ) || qgsgeometry_cast<const QgsMultiSurface *>( geom ) )
 
  596  : mSnapTolerance( snapTolerance )
 
 
  607  if ( !mFirstFeature )
 
  612    searchBounds.
grow( mSnapTolerance );
 
  614    if ( !refFeatureIds.isEmpty() )
 
  616      QList<QgsGeometry> refGeometries;
 
  617      const auto constRefFeatureIds = refFeatureIds;
 
  620        refGeometries << mProcessedGeometries.value( 
id );
 
  626  mProcessedGeometries.insert( feat.
id(), geometry );
 
  628  mFirstFeature = 
false;
 
 
Abstract base class for all geometries.
 
virtual int ringCount(int part=0) const =0
Returns the number of rings of which this geometry is built.
 
virtual bool moveVertex(QgsVertexId position, const QgsPoint &newPos)=0
Moves a vertex within the geometry.
 
virtual int vertexCount(int part=0, int ring=0) const =0
Returns the number of vertices of which this geometry is built.
 
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the geometry.
 
virtual QgsPoint vertexAt(QgsVertexId id) const =0
Returns the point corresponding to a specified vertex id.
 
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
 
virtual bool insertVertex(QgsVertexId position, const QgsPoint &vertex)=0
Inserts a vertex into the geometry.
 
virtual int partCount() const =0
Returns count of parts contained in the geometry.
 
virtual bool deleteVertex(QgsVertexId position)=0
Deletes a vertex within the geometry.
 
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
 
Abstract base class for curved geometry type.
 
Wrapper for iterator of features from vector data provider or vector layer.
 
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
 
Wraps a request for features to a vector layer (or directly its vector data provider).
 
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets the feature IDs that should be fetched.
 
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
 
An interface for objects which provide features via a getFeatures method.
 
virtual QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const =0
Returns an iterator for the features in the source.
 
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
 
bool hasGeometry() const
Returns true if the feature has an associated geometry.
 
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
 
void featureSnapped()
Emitted each time a feature has been processed when calling snapFeatures()
 
QgsFeatureList snapFeatures(const QgsFeatureList &features, double snapTolerance, SnapMode mode=PreferNodes)
Snaps a set of features to the reference layer and returns the result.
 
QgsGeometry snapGeometry(const QgsGeometry &geometry, double snapTolerance, SnapMode mode=PreferNodes) const
Snaps a geometry to the reference layer and returns the result.
 
@ EndPointPreferClosest
Only snap start/end points of lines (point features will also be snapped, polygon features will not b...
 
@ PreferClosestNoExtraVertices
Snap to closest point, regardless of it is a node or a segment. No new nodes will be inserted.
 
@ EndPointPreferNodes
Only snap start/end points of lines (point features will also be snapped, polygon features will not b...
 
@ PreferNodes
Prefer to snap to nodes, even when a segment may be closer than a node. New nodes will be inserted to...
 
@ PreferClosest
Snap to closest point, regardless of it is a node or a segment. New nodes will be inserted to make ge...
 
@ EndPointToEndPoint
Only snap the start/end points of lines to other start/end points of lines.
 
@ PreferNodesNoExtraVertices
Prefer to snap to nodes, even when a segment may be closer than a node. No new nodes will be inserted...
 
QgsGeometrySnapper(QgsFeatureSource *referenceSource)
Constructor for QgsGeometrySnapper.
 
static double sqrDistToLine(double ptX, double ptY, double x1, double y1, double x2, double y2, double &minDistX, double &minDistY, double epsilon)
Returns the squared distance between a point and a line.
 
static QgsPoint projectPointOnSegment(const QgsPoint &p, const QgsPoint &s1, const QgsPoint &s2)
Project the point on a segment.
 
static Q_DECL_DEPRECATED double sqrDistance2D(double x1, double y1, double x2, double y2)
Returns the squared 2D distance between (x1, y1) and (x2, y2).
 
A geometry is the spatial representation of a feature.
 
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
 
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...
 
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
 
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
 
static GEOSContextHandle_t get()
Returns a thread local instance of a GEOS context, safe for use in the current thread.
 
QgsInternalGeometrySnapper(double snapTolerance, QgsGeometrySnapper::SnapMode mode=QgsGeometrySnapper::PreferNodes)
Constructor for QgsInternalGeometrySnapper.
 
QgsGeometry snapFeature(const QgsFeature &feature)
Snaps a single feature's geometry against all feature geometries already processed by calls to snapFe...
 
Point geometry type, with support for z-dimension and m-values.
 
QgsPoint vertexAt(QgsVertexId) const override
Returns the point corresponding to a specified vertex id.
 
double distanceSquared(double x, double y) const
Returns the Cartesian 2D squared distance between this point a specified x, y coordinate.
 
A rectangle specified with double values.
 
void grow(double delta)
Grows the rectangle in place by the specified amount.
 
A spatial index for QgsFeature objects.
 
QList< QgsFeatureId > intersects(const QgsRectangle &rectangle) const
Returns a list of features with a bounding box which intersects the specified rectangle.
 
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a feature to the index.
 
Represent a 2-dimensional vector.
 
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
 
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
 
QList< QgsFeature > QgsFeatureList
 
QSet< QgsFeatureId > QgsFeatureIds
 
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
 
void _GEOSQueryCallback(void *item, void *userdata)
 
QLineF segment(int index, QRectF rect, double radius)
 
Utility class for identifying a unique vertex within a geometry.
 
QList< const QgsPointCloudLayerProfileResults::PointResult * > * list