27 , mEnableSnappingForInvisibleFeature( enableSnappingForInvisibleFeature )
42 if ( !mLocators.contains( vl ) )
46 mLocators.insert( vl, vlpl );
48 return mLocators.value( vl );
53 qDeleteAll( mLocators );
56 qDeleteAll( mTemporaryLocators );
57 mTemporaryLocators.clear();
66 QgsRectangle aoi( pointMap.
x() - tolerance, pointMap.
y() - tolerance,
67 pointMap.
x() + tolerance, pointMap.
y() + tolerance );
71 if ( loc->isIndexing() || isIndexPrepared( loc, aoi ) )
74 return temporaryLocatorForLayer( vl, pointMap, tolerance );
79 if ( mTemporaryLocators.contains( vl ) )
80 delete mTemporaryLocators.take( vl );
82 QgsRectangle rect( pointMap.
x() - tolerance, pointMap.
y() - tolerance,
83 pointMap.
x() + tolerance, pointMap.
y() + tolerance );
88 mTemporaryLocators.insert( vl, vlpl );
89 return mTemporaryLocators.value( vl );
107 if ( segments.isEmpty() )
110 QSet<QgsPointXY> endpoints;
113 QVector<QgsGeometry> geoms;
114 const auto constSegments = segments;
120 m.edgePoints( pl[0], pl[1] );
122 endpoints << pl[0] << pl[1];
129 QList<QgsPointXY> newPoints;
135 if ( !endpoints.contains( p ) )
144 const auto constPl = pl;
147 if ( !endpoints.contains( p ) )
153 if ( newPoints.isEmpty() )
158 double minSqrDist = 1e20;
159 const auto constNewPoints = newPoints;
162 double sqrDist = pt.
sqrDist( p.x(), p.y() );
163 if ( sqrDist < minSqrDist )
165 minSqrDist = sqrDist;
177 if ( !candidateMatch.
isValid() || candidateMatch.
distance() > maxDistance )
188 bestMatch = candidateMatch;
195 _replaceIfBetter( bestMatch, loc->
nearestVertex( pointMap, tolerance, filter, relaxed ), tolerance );
199 _replaceIfBetter( bestMatch, loc->
nearestEdge( pointMap, tolerance, filter, relaxed ), tolerance );
206 _replaceIfBetter( bestMatch, loc->
nearestArea( pointMap, tolerance, filter, relaxed ), tolerance );
234 return QgsRectangle( point.
x() - tolerance, point.
y() - tolerance,
235 point.
x() + tolerance, point.
y() + tolerance );
247 if ( !mCurrentLayer || mSnappingConfig.
type() == 0 )
252 QgsPointLocator::Types type = _snappingTypeToPointLocatorType( mSnappingConfig.
type() );
254 prepareIndex( QList<LayerAndAreaOfInterest>() << qMakePair( mCurrentLayer,
_areaOfInterest( pointMap, tolerance ) ), relaxed );
257 QgsPointLocator *loc = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
262 _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter, relaxed );
266 QgsPointLocator *locEdges = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
271 _replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), tolerance );
278 QList<LayerAndAreaOfInterest>
layers;
279 for (
const LayerConfig &layerConfig : qgis::as_const( mLayers ) )
282 layers << qMakePair( layerConfig.layer,
_areaOfInterest( pointMap, tolerance ) );
284 prepareIndex( layers, relaxed );
288 double maxSnapIntTolerance = 0;
290 for (
const LayerConfig &layerConfig : qgis::as_const( mLayers ) )
293 if (
QgsPointLocator *loc = locatorForLayerUsingStrategy( layerConfig.layer, pointMap, tolerance ) )
295 _updateBestMatch( bestMatch, pointMap, loc, layerConfig.type, tolerance, filter, relaxed );
300 maxSnapIntTolerance = std::max( maxSnapIntTolerance, tolerance );
306 _replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), maxSnapIntTolerance );
314 QgsPointLocator::Types type = _snappingTypeToPointLocatorType( mSnappingConfig.
type() );
317 QList<LayerAndAreaOfInterest>
layers;
318 const auto constLayers = mMapSettings.
layers();
320 if (
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
321 layers << qMakePair( vl, aoi );
322 prepareIndex( layers, relaxed );
327 for (
const LayerAndAreaOfInterest &entry : qgis::as_const( layers ) )
330 if (
QgsPointLocator *loc = locatorForLayerUsingStrategy( vl, pointMap, tolerance ) )
332 _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter, relaxed );
340 _replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), tolerance );
348 void QgsSnappingUtils::onInitFinished(
bool ok )
356 mHybridMaxAreaPerLayer[loc->
layer()->
id()] /= 4;
360 void QgsSnappingUtils::prepareIndex(
const QList<LayerAndAreaOfInterest> &
layers,
bool relaxed )
363 QList<LayerAndAreaOfInterest> layersToIndex;
364 const auto constLayers =
layers;
365 for (
const LayerAndAreaOfInterest &entry : constLayers )
374 if ( !loc->
isIndexing() && !isIndexPrepared( loc, entry.second ) )
375 layersToIndex << entry;
377 if ( !layersToIndex.isEmpty() )
389 for (
const LayerAndAreaOfInterest &entry : layersToIndex )
400 if ( !mEnableSnappingForInvisibleFeature )
410 loc->
init( -1, relaxed );
415 if ( !mHybridMaxAreaPerLayer.contains( vl->
id() ) )
418 if ( totalFeatureCount < mHybridPerLayerFeatureLimit )
421 mHybridMaxAreaPerLayer[vl->
id()] = -1;
428 double totalArea = layerExtent.
width() * layerExtent.
height();
429 mHybridMaxAreaPerLayer[vl->
id()] = totalArea * mHybridPerLayerFeatureLimit / totalFeatureCount / 4;
433 double indexReasonableArea = mHybridMaxAreaPerLayer[vl->
id()];
434 if ( indexReasonableArea == -1 )
437 loc->
init( -1, relaxed );
443 double halfSide = std::sqrt( indexReasonableArea ) / 2;
445 c.
x() + halfSide, c.
y() + halfSide );
449 loc->
init( mHybridPerLayerFeatureLimit, relaxed );
454 loc->
init( relaxed );
462 QgsDebugMsg( QStringLiteral(
"Prepare index total: %1 ms" ).arg( t.elapsed() ) );
469 return mSnappingConfig;
474 mEnableSnappingForInvisibleFeature = enable;
479 if ( mSnappingConfig == config )
498 if ( !mCurrentLayer )
504 QgsPointLocator *loc = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
509 _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter,
false );
517 mMapSettings = settings;
519 if ( newDestCRS != oldDestCRS )
525 mCurrentLayer = layer;
530 QString msg = QStringLiteral(
"--- SNAPPING UTILS DUMP ---\n" );
534 msg += QLatin1String(
"invalid map settings!" );
538 QList<LayerConfig>
layers;
544 msg += QLatin1String(
"no current layer!" );
548 layers <<
LayerConfig( mCurrentLayer, _snappingTypeToPointLocatorType( mSnappingConfig.
type() ), mSnappingConfig.
tolerance(), mSnappingConfig.
units() );
552 const auto constLayers = mMapSettings.
layers();
555 if (
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
556 layers <<
LayerConfig( vl, _snappingTypeToPointLocatorType( mSnappingConfig.
type() ), mSnappingConfig.
tolerance(), mSnappingConfig.
units() );
564 const auto constLayers =
layers;
567 msg += QString(
"layer : %1\n" 568 "config: %2 tolerance %3 %4\n" )
569 .arg( layer.layer->name() )
570 .arg( layer.type ).arg( layer.tolerance ).arg( layer.unit );
576 QString extentStr, cachedGeoms, limit( QStringLiteral(
"no max area" ) );
579 extentStr = QStringLiteral(
" extent %1" ).arg( r->toString() );
582 extentStr = QStringLiteral(
"full extent" );
586 cachedGeoms = QStringLiteral(
"not initialized" );
589 if ( mHybridMaxAreaPerLayer.contains( layer.layer->id() ) )
591 double maxArea = mStrategy ==
IndexHybrid ? mHybridMaxAreaPerLayer[layer.layer->id()] : -1;
593 limit = QStringLiteral(
"max area %1" ).arg( maxArea );
596 limit = QStringLiteral(
"not evaluated" );
598 msg += QStringLiteral(
"index : YES | %1 | %2 | %3\n" ).arg( cachedGeoms, extentStr, limit );
601 msg += QStringLiteral(
"index : ???\n" );
604 msg += QLatin1String(
"index : NO\n" );
605 msg += QLatin1String(
"-\n" );
616 void QgsSnappingUtils::onIndividualLayerSettingsChanged(
const QHash<QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings> &layerSettings )
620 QHash<QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings>::const_iterator i;
622 for ( i = layerSettings.constBegin(); i != layerSettings.constEnd(); ++i )
626 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:
void initFinished(bool ok)
Emitted whenever index has been built and initialization is finished.
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 with snapToMap. When index == count the indexing is complete...
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...
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.
void scale(double scaleFactor, const QgsPointXY *c=nullptr)
Scale the rectangle around its center point.
Q_INVOKABLE 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 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 ...
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 waitForIndexingFinished()
If the point locator has been initialized relaxedly and is currently indexing, this methods waits for...
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...
QgsPointLocator::Match snapToMap(QPoint point, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Snap to map according to the current configuration.
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
Encapsulates a QGIS project, including sets of map layers and their styles, layouts, annotations, canvases, etc.
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.
bool isIndexing() const
Returns true if the point locator is currently indexing the data.
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.
MatchList edgesInRect(const QgsRectangle &rect, QgsPointLocator::MatchFilter *filter=nullptr, bool relaxed=false)
Find edges within a specified recangle Optional filter may discard unwanted matches.
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 with snapToMap - can be overridden and e.g. progress dialog can be prov...
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.
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.
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
bool init(int maxFeaturesToIndex=-1, bool relaxed=false)
Prepare the index for queries.
This class represents a coordinate reference system (CRS).
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 ...
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.
This is a container for configuration of the snapping of the project.
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)
QgsVectorLayer * layer() const
Gets associated layer.
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.