17#include "moc_qgssnappingutils.cpp" 
   27  , mEnableSnappingForInvisibleFeature( enableSnappingForInvisibleFeature )
 
 
   42  if ( !mLocators.contains( vl ) )
 
   46    connect( vl, &QObject::destroyed, 
this, [
this, vl]()
 
   48      delete mLocators.take( vl );
 
   51    mLocators.insert( vl, vlpl );
 
   53  return mLocators.value( vl );
 
 
   58  qDeleteAll( mLocators );
 
   61  qDeleteAll( mTemporaryLocators );
 
   62  mTemporaryLocators.clear();
 
 
   71  QgsRectangle aoi( pointMap.
x() - tolerance, pointMap.
y() - tolerance,
 
   72                    pointMap.
x() + tolerance, pointMap.
y() + tolerance );
 
   76  if ( loc->
isIndexing() || isIndexPrepared( loc, aoi ) )
 
   79    return temporaryLocatorForLayer( vl, pointMap, tolerance );
 
   84  if ( mTemporaryLocators.contains( vl ) )
 
   85    delete mTemporaryLocators.take( vl );
 
   87  QgsRectangle rect( pointMap.
x() - tolerance, pointMap.
y() - tolerance,
 
   88                     pointMap.
x() + tolerance, pointMap.
y() + tolerance );
 
   93  mTemporaryLocators.insert( vl, vlpl );
 
   94  return mTemporaryLocators.value( vl );
 
  112  if ( segments.isEmpty() )
 
  115  QSet<QgsPointXY> endpoints;
 
  118  QVector<QgsGeometry> geoms;
 
  119  const auto constSegments = segments;
 
  125      m.edgePoints( pl[0], pl[1] );
 
  127      endpoints << pl[0] << pl[1];
 
  134  QList<QgsPointXY> newPoints;
 
  140      if ( !endpoints.contains( p ) )
 
  149      const auto constPl = pl;
 
  152        if ( !endpoints.contains( p ) )
 
  158  if ( newPoints.isEmpty() )
 
  163  double minSqrDist = 1e20;  
 
  164  const auto constNewPoints = newPoints;
 
  167    double sqrDist = pt.
sqrDist( p.x(), p.y() );
 
  168    if ( sqrDist < minSqrDist )
 
  170      minSqrDist = sqrDist;
 
  181  if ( !candidateMatch.
isValid() || candidateMatch.
distance() > maxDistance )
 
  201    bestMatch = candidateMatch;
 
  210    bestMatch = candidateMatch;
 
  222  bestMatch = candidateMatch; 
 
  229    _replaceIfBetter( bestMatch, loc->
nearestVertex( pointMap, tolerance, filter, relaxed ), tolerance );
 
  233    _replaceIfBetter( bestMatch, loc->
nearestEdge( pointMap, tolerance, filter, relaxed ), tolerance );
 
  240    _replaceIfBetter( bestMatch, loc->
nearestArea( pointMap, tolerance, filter, relaxed ), tolerance );
 
  244    _replaceIfBetter( bestMatch, loc->
nearestCentroid( pointMap, tolerance, filter, relaxed ), tolerance );
 
  248    _replaceIfBetter( bestMatch, loc->
nearestMiddleOfSegment( pointMap, tolerance, filter, relaxed ), tolerance );
 
  252    _replaceIfBetter( bestMatch, loc->
nearestLineEndpoints( pointMap, tolerance, filter, relaxed ), tolerance );
 
  269  return QgsRectangle( point.
x() - tolerance, point.
y() - tolerance,
 
  270                       point.
x() + tolerance, point.
y() + tolerance );
 
 
  289    prepareIndex( QList<LayerAndAreaOfInterest>() << qMakePair( mCurrentLayer, 
_areaOfInterest( pointMap, tolerance ) ), relaxed );
 
  292    QgsPointLocator *loc = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
 
  298    _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter, relaxed );
 
  300      edges = loc->
edgesInRect( pointMap, tolerance, filter, relaxed );
 
  304      QgsPointLocator *loc = locatorForLayerUsingStrategy( vl, pointMap, tolerance );
 
  305      _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter, 
false );
 
  307        edges << loc->
edgesInRect( pointMap, tolerance, filter, 
false );
 
  312      _replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), tolerance );
 
  319    QList<LayerAndAreaOfInterest> 
layers;
 
  320    QList<LayerConfig> filteredConfigs;
 
  328    for ( 
const LayerConfig &layerConfig : std::as_const( mLayers ) )
 
  343        filteredConfigs << layerConfig;
 
  346    prepareIndex( 
layers, relaxed );
 
  350    double maxTolerance = 0;
 
  353    for ( 
const LayerConfig &layerConfig : std::as_const( filteredConfigs ) )
 
  356      if ( 
QgsPointLocator *loc = locatorForLayerUsingStrategy( layerConfig.layer, pointMap, tolerance ) )
 
  358        _updateBestMatch( bestMatch, pointMap, loc, layerConfig.type, tolerance, filter, relaxed );
 
  360          edges << loc->
edgesInRect( pointMap, tolerance, filter, relaxed );
 
  363        maxTolerance = std::max( maxTolerance, tolerance );
 
  371      QgsPointLocator *loc = locatorForLayerUsingStrategy( vl, pointMap, maxTolerance );
 
  372      _updateBestMatch( bestMatch, pointMap, loc, maxTypes, maxTolerance, filter, 
false );
 
  374        edges << loc->
edgesInRect( pointMap, maxTolerance, filter, 
false );
 
  378      _replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), maxTolerance );
 
  389    QList<LayerAndAreaOfInterest> 
layers;
 
  390    const auto constLayers = mMapSettings.
layers( 
true );
 
  392      if ( 
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
 
  393        layers << qMakePair( vl, aoi );
 
  394    prepareIndex( 
layers, relaxed );
 
  399    for ( 
const LayerAndAreaOfInterest &entry : std::as_const( 
layers ) )
 
  402      if ( 
QgsPointLocator *loc = locatorForLayerUsingStrategy( vl, pointMap, tolerance ) )
 
  404        _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter, relaxed );
 
  406          edges << loc->
edgesInRect( pointMap, tolerance, filter, relaxed );
 
  412      QgsPointLocator *loc = locatorForLayerUsingStrategy( vl, pointMap, tolerance );
 
  413      _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter, 
false );
 
  415        edges << loc->
edgesInRect( pointMap, tolerance, filter, 
false );
 
  419      _replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), tolerance );
 
 
  427void QgsSnappingUtils::onInitFinished( 
bool ok )
 
  435    mHybridMaxAreaPerLayer[loc->
layer()->
id()] /= 4;
 
  439void QgsSnappingUtils::prepareIndex( 
const QList<LayerAndAreaOfInterest> &layers, 
bool relaxed )
 
  442  QList<LayerAndAreaOfInterest> layersToIndex;
 
  443  const auto constLayers = 
layers;
 
  444  for ( 
const LayerAndAreaOfInterest &entry : constLayers )
 
  453    if ( !loc->
isIndexing() && !isIndexPrepared( loc, entry.second ) )
 
  454      layersToIndex << entry;
 
  456  if ( !layersToIndex.isEmpty() )
 
  468    for ( 
const LayerAndAreaOfInterest &entry : layersToIndex )
 
  479      if ( !mEnableSnappingForInvisibleFeature )
 
  489        loc->
init( -1, relaxed );
 
  494        if ( !mHybridMaxAreaPerLayer.contains( vl->
id() ) )
 
  497          if ( totalFeatureCount < mHybridPerLayerFeatureLimit )
 
  500            mHybridMaxAreaPerLayer[vl->
id()] = -1;
 
  507            double totalArea = layerExtent.
width() * layerExtent.
height();
 
  508            mHybridMaxAreaPerLayer[vl->
id()] = totalArea * mHybridPerLayerFeatureLimit / totalFeatureCount / 4;
 
  512        double indexReasonableArea = mHybridMaxAreaPerLayer[vl->
id()];
 
  513        if ( indexReasonableArea == -1 )
 
  516          loc->
init( -1, relaxed );
 
  522          double halfSide = std::sqrt( indexReasonableArea ) / 2;
 
  524                             c.x() + halfSide, 
c.y() + halfSide );
 
  528          loc->
init( mHybridPerLayerFeatureLimit, relaxed );
 
  533        loc->
init( relaxed );
 
  541      QgsDebugMsgLevel( QStringLiteral( 
"Prepare index total: %1 ms" ).arg( t.elapsed() ), 2 );
 
  548  return mSnappingConfig;
 
 
  553  mEnableSnappingForInvisibleFeature = enable;
 
 
  558  if ( mSnappingConfig == 
config )
 
 
  577  if ( !mCurrentLayer )
 
  583  QgsPointLocator *loc = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
 
  588  _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter, 
false );
 
 
  596  mMapSettings = settings;
 
  598  if ( newDestCRS != oldDestCRS )
 
 
  604  mCurrentLayer = layer;
 
 
  609  QString msg = QStringLiteral( 
"--- SNAPPING UTILS DUMP ---\n" );
 
  613    msg += QLatin1String( 
"invalid map settings!" );
 
  617  QList<LayerConfig> 
layers;
 
  623      msg += QLatin1String( 
"no current layer!" );
 
  631    const auto constLayers = mMapSettings.
layers( 
true );
 
  634      if ( 
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
 
  643  const auto constLayers = 
layers;
 
  646    msg += QString( 
"layer : %1\n" 
  647                    "config: %2   tolerance %3 %4\n" )
 
  648           .arg( layer.layer->name() )
 
  649           .arg( layer.type ).arg( layer.tolerance ).arg( 
static_cast<int>( layer.unit ) );
 
  655        QString extentStr, cachedGeoms, limit( QStringLiteral( 
"no max area" ) );
 
  658          extentStr = QStringLiteral( 
" extent %1" ).arg( r->toString() );
 
  661          extentStr = QStringLiteral( 
"full extent" );
 
  665          cachedGeoms = QStringLiteral( 
"not initialized" );
 
  668          if ( mHybridMaxAreaPerLayer.contains( layer.layer->id() ) )
 
  670            double maxArea = mStrategy == 
IndexHybrid ? mHybridMaxAreaPerLayer[layer.layer->id()] : -1;
 
  672              limit = QStringLiteral( 
"max area %1" ).arg( maxArea );
 
  675            limit = QStringLiteral( 
"not evaluated" );
 
  677        msg += QStringLiteral( 
"index : YES | %1 | %2 | %3\n" ).arg( cachedGeoms, extentStr, limit );
 
  680        msg += QLatin1String( 
"index : ???\n" ); 
 
  683      msg += QLatin1String( 
"index : NO\n" );
 
  684    msg += QLatin1String( 
"-\n" );
 
 
  695void QgsSnappingUtils::onIndividualLayerSettingsChanged( 
const QHash<QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings> &layerSettings )
 
  699  QHash<QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings>::const_iterator i;
 
  701  for ( i = layerSettings.constBegin(); i != layerSettings.constEnd(); ++i )
 
  705      mLayers.append( LayerConfig( i.key(), _snappingTypeToPointLocatorType( 
static_cast<Qgis::SnappingTypes>( i->typeFlag() ) ), i->tolerance(), i->units() ) );
 
QFlags< SnappingType > SnappingTypes
Snapping types.
 
@ ActiveLayer
On the active layer.
 
@ AdvancedConfiguration
On a per layer configuration basis.
 
@ AllLayers
On all vector layers.
 
@ MultiLineString
MultiLineString.
 
Represents a coordinate reference system (CRS).
 
A geometry is the spatial representation of a feature.
 
static QgsGeometry fromPolylineXY(const QgsPolylineXY &polyline)
Creates a new LineString geometry from a list of QgsPointXY points.
 
QgsPolylineXY asPolyline() const
Returns the contents of the geometry as a polyline.
 
QgsMultiPolylineXY asMultiPolyline() const
Returns the contents of the geometry as a multi-linestring.
 
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries, const QgsGeometryParameters ¶meters=QgsGeometryParameters())
Compute the unary union on a list of geometries.
 
Qgis::WkbType wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
 
Base class for all map layer types.
 
Contains configuration for rendering maps.
 
QList< QgsMapLayer * > layers(bool expandGroupLayers=false) const
Returns the list of layers which will be rendered in the map.
 
double scale() const
Returns the calculated map scale.
 
QgsRectangle layerExtentToOutputExtent(const QgsMapLayer *layer, QgsRectangle extent) const
transform bounding box from layer's CRS to output CRS
 
const QgsMapToPixel & mapToPixel() const
 
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes output image size into account.
 
bool hasValidSettings() const
Check whether the map settings are valid and can be used for rendering.
 
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
 
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
 
QgsPointXY toMapCoordinates(int x, int y) const
Transforms device coordinates to map (world) coordinates.
 
Defines the interface for querying point locations.
 
Match nearestEdge(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find nearest edge to the specified point - up to distance specified by tolerance Optional filter may ...
 
void setRenderContext(const QgsRenderContext *context)
Configure render context - if not nullptr, it will use to index only visible feature.
 
int cachedGeometryCount() const
Returns how many geometries are cached in the index.
 
void setExtent(const QgsRectangle *extent)
Configure extent - if not nullptr, it will index only that area.
 
bool init(int maxFeaturesToIndex=-1, bool relaxed=false)
Prepare the index for queries.
 
QgsVectorLayer * layer() const
Gets associated layer.
 
class QList< QgsPointLocator::Match > MatchList
 
Match nearestVertex(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find nearest vertex to the specified point - up to distance specified by tolerance Optional filter ma...
 
bool isIndexing() const
Returns true if the point locator is currently indexing the data.
 
const QgsRectangle * extent() const
Gets extent of the area point locator covers - if nullptr then it caches the whole layer.
 
MatchList edgesInRect(const QgsRectangle &rect, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find edges within a specified rectangle Optional filter may discard unwanted matches.
 
bool hasIndex() const
Indicate whether the data have been already indexed.
 
Match nearestMiddleOfSegment(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find nearest middle of segment to the specified point - up to distance specified by tolerance Optiona...
 
void waitForIndexingFinished()
If the point locator has been initialized relaxedly and is currently indexing, this methods waits for...
 
Match nearestCentroid(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find nearest centroid to the specified point - up to distance specified by tolerance Optional filter ...
 
void initFinished(bool ok)
Emitted whenever index has been built and initialization is finished.
 
Match nearestArea(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find nearest area to the specified point - up to distance specified by tolerance Optional filter may ...
 
Type
The type of a snap result or the filter type for a snap request.
 
@ Area
Snapped to an area.
 
@ MiddleOfSegment
Snapped to the middle of a segment.
 
@ Vertex
Snapped to a vertex. Can be a vertex of the geometry or an intersection.
 
@ Centroid
Snapped to a centroid.
 
@ Edge
Snapped to an edge.
 
@ LineEndpoint
Start or end points of lines only.
 
Match nearestLineEndpoints(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find nearest line endpoint (start or end vertex) to the specified point - up to distance specified by...
 
double sqrDist(double x, double y) const
Returns the squared distance between this point a specified x, y coordinate.
 
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
 
A rectangle specified with double values.
 
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
 
bool intersects(const QgsRectangle &rect) const
Returns true when rectangle intersects with other rectangle.
 
Contains information about the context of a rendering operation.
 
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
 
A container of advanced configuration (per layer) of the snapping of the project.
 
double maximumScale() const
Returns max scale on which snapping is limited.
 
double minimumScale() const
Returns minimum scale on which snapping is limited.
 
Stores configuration of snapping settings for the project.
 
bool intersectionSnapping() const
Returns if the snapping on intersection is enabled.
 
@ PerLayer
Scale dependency using min max range per layer.
 
@ Disabled
No scale dependency.
 
@ Global
Scale dependency using global min max range.
 
double minimumScale() const
Returns the min scale (i.e.
 
double tolerance() const
Returns the tolerance.
 
Qgis::SnappingTypes typeFlag() const
Returns the flags type (vertices | segments | area | centroid | middle)
 
double maximumScale() const
Returns the max scale (i.e.
 
Qgis::MapToolUnit units() const
Returns the type of units.
 
QHash< QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings > individualLayerSettings() const
Returns individual snapping settings for all layers.
 
ScaleDependencyMode scaleDependencyMode() const
Returns the scale dependency mode.
 
void setEnabled(bool enabled)
enables the snapping
 
QgsPointLocator::Match snapToCurrentLayer(QPoint point, QgsPointLocator::Types type, QgsPointLocator::MatchFilter *filter=nullptr)
Snap to current layer.
 
void setMapSettings(const QgsMapSettings &settings)
Assign current map settings to the utils - used for conversion between screen coords to map coords.
 
void toggleEnabled()
Toggles the state of snapping.
 
@ IndexAlwaysFull
For all layers build index of full extent. Uses more memory, but queries are faster.
 
@ IndexHybrid
For "big" layers using IndexNeverFull, for the rest IndexAlwaysFull. Compromise between speed and mem...
 
@ IndexExtent
For all layer build index of extent given in map settings.
 
@ IndexNeverFull
For all layers only create temporary indexes of small extent. Low memory usage, slower queries.
 
QgsPointLocator * locatorForLayer(QgsVectorLayer *vl)
Gets a point locator for the given layer.
 
virtual void prepareIndexProgress(int index)
Called when finished indexing a layer with snapToMap. When index == count the indexing is complete.
 
QString dump()
Gets extra information about the instance.
 
void configChanged(const QgsSnappingConfig &snappingConfig)
Emitted when the snapping settings object changes.
 
~QgsSnappingUtils() override
 
void clearAllLocators()
Deletes all existing locators (e.g. when destination CRS has changed and we need to reindex)
 
void setCurrentLayer(QgsVectorLayer *layer)
Sets current layer so that if mode is SnapCurrentLayer we know which layer to use.
 
QgsPointLocator::Match snapToMap(QPoint point, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Snap to map according to the current configuration.
 
QgsSnappingUtils(QObject *parent=nullptr, bool enableSnappingForInvisibleFeature=true)
Constructor for QgsSnappingUtils.
 
void setConfig(const QgsSnappingConfig &snappingConfig)
The snapping configuration controls the behavior of this object.
 
void setEnableSnappingForInvisibleFeature(bool enable)
Set if invisible features must be snapped or not.
 
QList< QgsSnappingUtils::LayerConfig > layers() const
Query layers used for snapping.
 
virtual void prepareIndexStarting(int count)
Called when starting to index with snapToMap - can be overridden and e.g. progress dialog can be prov...
 
static double vertexSearchRadius(const QgsMapSettings &mapSettings)
Static function to get vertex tolerance value.
 
static double toleranceInProjectUnits(double tolerance, QgsMapLayer *layer, const QgsMapSettings &mapSettings, Qgis::MapToolUnit units)
Static function to translate tolerance value into map units.
 
Represents a vector layer which manages a vector based dataset.
 
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
 
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
 
QgsRectangle extent() const FINAL
Returns the extent of the layer.
 
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
 
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
 
#define QgsDebugMsgLevel(str, level)
 
QgsRectangle _areaOfInterest(const QgsPointXY &point, double tolerance)
 
Interface that allows rejection of some matches in intersection queries (e.g.
 
double distance() const
for vertex / edge match units depending on what class returns it (geom.cache: layer units,...
 
QgsPointLocator::Type type() const
 
Configures how a certain layer should be handled in a snapping operation.