27 , mEnableSnappingForInvisibleFeature( enableSnappingForInvisibleFeature )
42 if ( !mLocators.contains( vl ) )
45 mLocators.insert( vl, vlpl );
47 return mLocators.value( vl );
52 qDeleteAll( mLocators );
55 qDeleteAll( mTemporaryLocators );
56 mTemporaryLocators.clear();
62 QgsRectangle aoi( pointMap.
x() - tolerance, pointMap.
y() - tolerance,
63 pointMap.
x() + tolerance, pointMap.
y() + tolerance );
64 if ( isIndexPrepared( vl, aoi ) )
67 return temporaryLocatorForLayer( vl, pointMap, tolerance );
72 if ( mTemporaryLocators.contains( vl ) )
73 delete mTemporaryLocators.take( vl );
75 QgsRectangle rect( pointMap.
x() - tolerance, pointMap.
y() - tolerance,
76 pointMap.
x() + tolerance, pointMap.
y() + tolerance );
78 mTemporaryLocators.insert( vl, vlpl );
79 return mTemporaryLocators.value( vl );
102 if ( segments.isEmpty() )
105 QSet<QgsPointXY> endpoints;
108 QVector<QgsGeometry> geoms;
109 const auto constSegments = segments;
115 m.edgePoints( pl[0], pl[1] );
117 endpoints << pl[0] << pl[1];
124 QList<QgsPointXY> newPoints;
130 if ( !endpoints.contains( p ) )
139 const auto constPl = pl;
142 if ( !endpoints.contains( p ) )
148 if ( newPoints.isEmpty() )
153 double minSqrDist = 1e20;
154 const auto constNewPoints = newPoints;
157 double sqrDist = pt.
sqrDist( p.x(), p.y() );
158 if ( sqrDist < minSqrDist )
160 minSqrDist = sqrDist;
172 if ( !candidateMatch.
isValid() || candidateMatch.
distance() > maxDistance )
183 bestMatch = candidateMatch;
190 _replaceIfBetter( bestMatch, loc->
nearestVertex( pointMap, tolerance, filter ), tolerance );
194 _replaceIfBetter( bestMatch, loc->
nearestEdge( pointMap, tolerance, filter ), tolerance );
201 _replaceIfBetter( bestMatch, loc->
nearestArea( pointMap, tolerance, filter ), tolerance );
230 return QgsRectangle( point.
x() - tolerance, point.
y() - tolerance,
231 point.
x() + tolerance, point.
y() + tolerance );
243 if ( !mCurrentLayer || mSnappingConfig.
type() == 0 )
248 QgsPointLocator::Types type = _snappingTypeToPointLocatorType( mSnappingConfig.
type() );
250 prepareIndex( QList<LayerAndAreaOfInterest>() << qMakePair( mCurrentLayer,
_areaOfInterest( pointMap, tolerance ) ) );
253 QgsPointLocator *loc = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
258 _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter );
262 QgsPointLocator *locEdges = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
264 _replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), tolerance );
271 QList<LayerAndAreaOfInterest>
layers;
272 for (
const LayerConfig &layerConfig : qgis::as_const( mLayers ) )
275 layers << qMakePair( layerConfig.layer,
_areaOfInterest( pointMap, tolerance ) );
277 prepareIndex( layers );
281 double maxSnapIntTolerance = 0;
283 for (
const LayerConfig &layerConfig : qgis::as_const( mLayers ) )
286 if (
QgsPointLocator *loc = locatorForLayerUsingStrategy( layerConfig.layer, pointMap, tolerance ) )
288 _updateBestMatch( bestMatch, pointMap, loc, layerConfig.type, tolerance, filter );
293 maxSnapIntTolerance = std::max( maxSnapIntTolerance, tolerance );
299 _replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), maxSnapIntTolerance );
307 QgsPointLocator::Types type = _snappingTypeToPointLocatorType( mSnappingConfig.
type() );
310 QList<LayerAndAreaOfInterest>
layers;
311 const auto constLayers = mMapSettings.
layers();
313 if (
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
314 layers << qMakePair( vl, aoi );
315 prepareIndex( layers );
320 for (
const LayerAndAreaOfInterest &entry : qgis::as_const( layers ) )
323 if (
QgsPointLocator *loc = locatorForLayerUsingStrategy( vl, pointMap, tolerance ) )
325 _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter );
333 _replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), tolerance );
341 void QgsSnappingUtils::prepareIndex(
const QList<LayerAndAreaOfInterest> &
layers )
348 QList<LayerAndAreaOfInterest> layersToIndex;
349 const auto constLayers =
layers;
350 for (
const LayerAndAreaOfInterest &entry : constLayers )
357 if ( !isIndexPrepared( vl, entry.second ) )
358 layersToIndex << entry;
360 if ( !layersToIndex.isEmpty() )
367 const auto constLayersToIndex = layersToIndex;
368 for (
const LayerAndAreaOfInterest &entry : constLayersToIndex )
376 if ( !mEnableSnappingForInvisibleFeature )
391 if ( !mHybridMaxAreaPerLayer.contains( vl->
id() ) )
394 if ( totalFeatureCount < mHybridPerLayerFeatureLimit )
397 mHybridMaxAreaPerLayer[vl->
id()] = -1;
404 double totalArea = layerExtent.
width() * layerExtent.
height();
405 mHybridMaxAreaPerLayer[vl->
id()] = totalArea * mHybridPerLayerFeatureLimit / totalFeatureCount / 4;
409 double indexReasonableArea = mHybridMaxAreaPerLayer[vl->
id()];
410 if ( indexReasonableArea == -1 )
419 double halfSide = std::sqrt( indexReasonableArea ) / 2;
421 c.
x() + halfSide, c.
y() + halfSide );
425 if ( !loc->
init( mHybridPerLayerFeatureLimit ) )
429 mHybridMaxAreaPerLayer[vl->
id()] /= 4;
437 QgsDebugMsg( QStringLiteral(
"Index init: %1 ms (%2)" ).arg( tt.elapsed() ).arg( vl->
id() ) );
440 QgsDebugMsg( QStringLiteral(
"Prepare index total: %1 ms" ).arg( t.elapsed() ) );
447 return mSnappingConfig;
452 mEnableSnappingForInvisibleFeature = enable;
457 if ( mSnappingConfig == config )
476 if ( !mCurrentLayer )
482 QgsPointLocator *loc = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
487 _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter );
495 mMapSettings = settings;
497 if ( newDestCRS != oldDestCRS )
503 mCurrentLayer = layer;
508 QString msg = QStringLiteral(
"--- SNAPPING UTILS DUMP ---\n" );
512 msg += QLatin1String(
"invalid map settings!" );
516 QList<LayerConfig>
layers;
522 msg += QLatin1String(
"no current layer!" );
526 layers <<
LayerConfig( mCurrentLayer, _snappingTypeToPointLocatorType( mSnappingConfig.
type() ), mSnappingConfig.
tolerance(), mSnappingConfig.
units() );
530 const auto constLayers = mMapSettings.
layers();
533 if (
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
534 layers <<
LayerConfig( vl, _snappingTypeToPointLocatorType( mSnappingConfig.
type() ), mSnappingConfig.
tolerance(), mSnappingConfig.
units() );
542 const auto constLayers =
layers;
545 msg += QString(
"layer : %1\n" 546 "config: %2 tolerance %3 %4\n" )
547 .arg( layer.layer->name() )
548 .arg( layer.type ).arg( layer.tolerance ).arg( layer.unit );
554 QString extentStr, cachedGeoms, limit( QStringLiteral(
"no max area" ) );
557 extentStr = QStringLiteral(
" extent %1" ).arg( r->toString() );
560 extentStr = QStringLiteral(
"full extent" );
564 cachedGeoms = QStringLiteral(
"not initialized" );
567 if ( mHybridMaxAreaPerLayer.contains( layer.layer->id() ) )
569 double maxArea = mStrategy ==
IndexHybrid ? mHybridMaxAreaPerLayer[layer.layer->id()] : -1;
571 limit = QStringLiteral(
"max area %1" ).arg( maxArea );
574 limit = QStringLiteral(
"not evaluated" );
576 msg += QStringLiteral(
"index : YES | %1 | %2 | %3\n" ).arg( cachedGeoms, extentStr, limit );
579 msg += QStringLiteral(
"index : ???\n" );
582 msg += QLatin1String(
"index : NO\n" );
583 msg += QLatin1String(
"-\n" );
594 void QgsSnappingUtils::onIndividualLayerSettingsChanged(
const QHash<QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings> &layerSettings )
598 QHash<QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings>::const_iterator i;
600 for ( i = layerSettings.constBegin(); i != layerSettings.constEnd(); ++i )
604 mLayers.append(
LayerConfig( i.key(), _snappingTypeToPointLocatorType( i->type() ), i->tolerance(), i->units() ) );
void setEnabled(bool enabled)
enables the snapping
The class defines interface for querying point location:
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
A rectangle specified with double values.
Base class for all map layer types.
static QgsGeometry fromPolylineXY(const QgsPolylineXY &polyline)
Creates a new LineString geometry from a list of QgsPointXY points.
SnappingType type() const
Returns the type (vertices and/or segments)
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
SnappingMode mode() const
Returns the mode (all layers, active layer, per layer settings)
void setCurrentLayer(QgsVectorLayer *layer)
Sets current layer so that if mode is SnapCurrentLayer we know which layer to use.
~QgsSnappingUtils() override
virtual void prepareIndexProgress(int index)
Called when finished indexing a layer. When index == count the indexing is complete.
QgsPointLocator::Type type() const
Both on vertices and segments.
bool enabled() const
Returns if snapping is enabled.
bool hasIndex() const
Indicate whether the data have been already indexed.
QgsWkbTypes::Type wkbType() const
Returns type of the geometry as a WKB type (point / linestring / polygon etc.)
For all layer build index of extent given in map settings.
A class to represent a 2D point.
Match nearestEdge(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr)
Find nearest edge to the specified point - up to distance specified by tolerance Optional filter may ...
void scale(double scaleFactor, const QgsPointXY *c=nullptr)
Scale the rectangle around its center point.
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
class QList< QgsPointLocator::Match > MatchList
A geometry is the spatial representation of a feature.
QgsTolerance::UnitType units() const
Returns the type of units.
Match nearestArea(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr)
Find nearest area to the specified point - up to distance specified by tolerance Optional filter may ...
QList< QgsMapLayer * > layers() const
Gets list of layers for map rendering The layers are stored in the reverse order of how they are rend...
Interface that allows rejection of some matches in intersection queries (e.g.
void setEnableSnappingForInvisibleFeature(bool enable)
Set if invisible features must be snapped or not.
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes takes output image size into accou...
bool intersectionSnapping() const
Returns if the snapping on intersection is enabled.
QgsCoordinateReferenceSystem destinationCrs() const
returns CRS of destination coordinate reference system
The QgsMapSettings class contains configuration for rendering of the map.
double sqrDist(double x, double y) const
Returns the squared distance between this point a specified x, y coordinate.
QgsMultiPolylineXY asMultiPolyline() const
Returns the contents of the geometry as a multi-linestring.
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
For all layers build index of full extent. Uses more memory, but queries are faster.
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
QString dump()
Gets extra information about the instance.
For "big" layers using IndexNeverFull, for the rest IndexAlwaysFull. Compromise between speed and mem...
void clearAllLocators()
Deletes all existing locators (e.g. when destination CRS has changed and we need to reindex) ...
long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
void setRenderContext(const QgsRenderContext *context)
Configure render context - if not nullptr, it will use to index only visible feature.
double width() const
Returns the width of the rectangle.
QList< QgsSnappingUtils::LayerConfig > layers() const
Query layers used for snapping.
Snapped to a vertex. Can be a vertex of the geometry or an intersection.
QgsRectangle layerExtentToOutputExtent(const QgsMapLayer *layer, QgsRectangle extent) const
transform bounding box from layer's CRS to output CRS
Reads and writes project states.
On a per layer configuration basis.
void setExtent(const QgsRectangle *extent)
Configure extent - if not nullptr, it will index only that area.
bool hasValidSettings() const
Check whether the map settings are valid and can be used for rendering.
void toggleEnabled()
Toggles the state of snapping.
void setMapSettings(const QgsMapSettings &settings)
Assign current map settings to the utils - used for conversion between screen coords to map coords...
const QgsMapToPixel & mapToPixel() const
const QgsRectangle * extent() const
Gets extent of the area point locator covers - if nullptr then it caches the whole layer...
QgsSnappingConfig config() const
The snapping configuration controls the behavior of this object.
QgsRectangle extent() const FINAL
Returns the extent of the layer.
For all layers only create temporary indexes of small extent. Low memory usage, slower queries...
void configChanged(const QgsSnappingConfig &snappingConfig)
Emitted when the snapping settings object changes.
static double vertexSearchRadius(const QgsMapSettings &mapSettings)
Static function to get vertex tolerance value.
int cachedGeometryCount() const
Returns how many geometries are cached in the index.
virtual void prepareIndexStarting(int count)
Called when starting to index - can be overridden and e.g. progress dialog can be provided...
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
Contains information about the context of a rendering operation.
QgsPointLocator * locatorForLayer(QgsVectorLayer *vl)
Gets a point locator for the given layer. If such locator does not exist, it will be created...
SnappingType
SnappingType defines on what object the snapping is performed.
static double toleranceInProjectUnits(double tolerance, QgsMapLayer *layer, const QgsMapSettings &mapSettings, QgsTolerance::UnitType units)
Static function to translate tolerance value into map units.
Configures how a certain layer should be handled in a snapping operation.
bool init(int maxFeaturesToIndex=-1)
Prepare the index for queries.
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
This class represents a coordinate reference system (CRS).
Match nearestVertex(const QgsPointXY &point, double tolerance, QgsPointLocator::MatchFilter *filter=nullptr)
Find nearest vertex to the specified point - up to distance specified by tolerance Optional filter ma...
double distance() const
for vertex / edge match units depending on what class returns it (geom.cache: layer units...
QgsPolylineXY asPolyline() const
Returns the contents of the geometry as a polyline.
double tolerance() const
Returns the tolerance.
MatchList edgesInRect(const QgsRectangle &rect, QgsPointLocator::MatchFilter *filter=nullptr)
Find edges within a specified recangle Optional filter may discard unwanted matches.
This is a container for configuration of the snapping of the project.
QgsPointLocator::Match snapToMap(QPoint point, QgsPointLocator::MatchFilter *filter=nullptr)
Snap to map according to the current configuration. Optional filter allows discarding unwanted matche...
bool intersects(const QgsRectangle &rect) const
Returns true when rectangle intersects with other rectangle.
QHash< QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings > individualLayerSettings() const
Returns individual snapping settings for all layers.
Represents a vector layer which manages a vector based data sets.
QgsRectangle _areaOfInterest(const QgsPointXY &point, double tolerance)
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries)
Compute the unary union on a list of geometries.
QgsSnappingUtils(QObject *parent=nullptr, bool enableSnappingForInvisibleFeature=true)
Constructor for QgsSnappingUtils.
void setConfig(const QgsSnappingConfig &snappingConfig)
The snapping configuration controls the behavior of this object.
QgsPointXY toMapCoordinates(int x, int y) const
Transform device coordinates to map (world) coordinates.
QString authid() const
Returns the authority identifier for the CRS.
double height() const
Returns the height of the rectangle.
QgsPointLocator::Match snapToCurrentLayer(QPoint point, QgsPointLocator::Types type, QgsPointLocator::MatchFilter *filter=nullptr)
Snap to current layer.