40 if ( !mLocators.contains( vl ) )
43 mLocators.insert( vl, vlpl );
45 return mLocators.value( vl );
50 qDeleteAll( mLocators );
53 qDeleteAll( mTemporaryLocators );
54 mTemporaryLocators.clear();
60 QgsRectangle aoi( pointMap.
x() - tolerance, pointMap.
y() - tolerance,
61 pointMap.
x() + tolerance, pointMap.
y() + tolerance );
62 if ( isIndexPrepared( vl, aoi ) )
65 return temporaryLocatorForLayer( vl, pointMap, tolerance );
70 if ( mTemporaryLocators.contains( vl ) )
71 delete mTemporaryLocators.take( vl );
73 QgsRectangle rect( pointMap.
x() - tolerance, pointMap.
y() - tolerance,
74 pointMap.
x() + tolerance, pointMap.
y() + tolerance );
76 mTemporaryLocators.insert( vl, vlpl );
77 return mTemporaryLocators.value( vl );
98 if ( segments.isEmpty() )
101 QSet<QgsPointXY> endpoints;
104 QVector<QgsGeometry> geoms;
112 endpoints << pl[0] << pl[1];
119 QList<QgsPointXY> newPoints;
124 if ( !endpoints.contains( p ) )
134 if ( !endpoints.contains( p ) )
140 if ( newPoints.isEmpty() )
145 double minSqrDist = 1e20;
148 double sqrDist = pt.
sqrDist( p.
x(), p.
y() );
149 if ( sqrDist < minSqrDist )
151 minSqrDist = sqrDist;
163 if ( !candidateMatch.
isValid() || candidateMatch.
distance() > maxDistance )
174 bestMatch = candidateMatch;
182 _replaceIfBetter( bestMatch, loc->
nearestVertex( pointMap, tolerance, filter ), tolerance );
186 _replaceIfBetter( bestMatch, loc->
nearestEdge( pointMap, tolerance, filter ), tolerance );
193 _replaceIfBetter( bestMatch, loc->
nearestArea( pointMap, tolerance, filter ), tolerance );
222 return QgsRectangle( point.
x() - tolerance, point.
y() - tolerance,
223 point.
x() + tolerance, point.
y() + tolerance );
235 if ( !mCurrentLayer || mSnappingConfig.
type() == 0 )
240 QgsPointLocator::Types type = _snappingTypeToPointLocatorType( mSnappingConfig.
type() );
242 prepareIndex( QList<LayerAndAreaOfInterest>() << qMakePair( mCurrentLayer,
_areaOfInterest( pointMap, tolerance ) ) );
245 QgsPointLocator *loc = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
250 _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter );
254 QgsPointLocator *locEdges = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
256 _replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), tolerance );
263 QList<LayerAndAreaOfInterest>
layers;
264 Q_FOREACH (
const LayerConfig &layerConfig, mLayers )
269 prepareIndex( layers );
273 double maxSnapIntTolerance = 0;
275 Q_FOREACH (
const LayerConfig &layerConfig, mLayers )
278 if (
QgsPointLocator *loc = locatorForLayerUsingStrategy( layerConfig.
layer, pointMap, tolerance ) )
280 _updateBestMatch( bestMatch, pointMap, loc, layerConfig.
type, tolerance, filter );
285 maxSnapIntTolerance = std::max( maxSnapIntTolerance, tolerance );
291 _replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), maxSnapIntTolerance );
299 QgsPointLocator::Types type = _snappingTypeToPointLocatorType( mSnappingConfig.
type() );
302 QList<LayerAndAreaOfInterest>
layers;
304 if (
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
305 layers << qMakePair( vl, aoi );
306 prepareIndex( layers );
311 Q_FOREACH (
const LayerAndAreaOfInterest &entry, layers )
314 if (
QgsPointLocator *loc = locatorForLayerUsingStrategy( vl, pointMap, tolerance ) )
316 _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter );
324 _replaceIfBetter( bestMatch, _findClosestSegmentIntersection( pointMap, edges ), tolerance );
333 void QgsSnappingUtils::prepareIndex(
const QList<LayerAndAreaOfInterest> &
layers )
340 QList<LayerAndAreaOfInterest> layersToIndex;
341 Q_FOREACH (
const LayerAndAreaOfInterest &entry, layers )
347 if ( !isIndexPrepared( vl, entry.second ) )
348 layersToIndex << entry;
350 if ( !layersToIndex.isEmpty() )
357 Q_FOREACH (
const LayerAndAreaOfInterest &entry, layersToIndex )
372 if ( !mHybridMaxAreaPerLayer.contains( vl->
id() ) )
375 if ( totalFeatureCount < mHybridPerLayerFeatureLimit )
378 mHybridMaxAreaPerLayer[vl->
id()] = -1;
385 double totalArea = layerExtent.
width() * layerExtent.
height();
386 mHybridMaxAreaPerLayer[vl->
id()] = totalArea * mHybridPerLayerFeatureLimit / totalFeatureCount / 4;
390 double indexReasonableArea = mHybridMaxAreaPerLayer[vl->
id()];
391 if ( indexReasonableArea == -1 )
400 double halfSide = std::sqrt( indexReasonableArea ) / 2;
402 c.
x() + halfSide, c.
y() + halfSide );
406 if ( !loc->
init( mHybridPerLayerFeatureLimit ) )
410 mHybridMaxAreaPerLayer[vl->
id()] /= 4;
418 QgsDebugMsg( QString(
"Index init: %1 ms (%2)" ).arg( tt.elapsed() ).arg( vl->
id() ) );
421 QgsDebugMsg( QString(
"Prepare index total: %1 ms" ).arg( t.elapsed() ) );
428 return mSnappingConfig;
433 if ( mSnappingConfig == config )
452 if ( !mCurrentLayer )
458 QgsPointLocator *loc = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
463 _updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter );
471 mMapSettings = settings;
473 if ( newDestCRS != oldDestCRS )
479 mCurrentLayer = layer;
484 QString msg = QStringLiteral(
"--- SNAPPING UTILS DUMP ---\n" );
488 msg += QLatin1String(
"invalid map settings!" );
492 QList<LayerConfig>
layers;
498 msg += QLatin1String(
"no current layer!" );
502 layers <<
LayerConfig( mCurrentLayer, _snappingTypeToPointLocatorType( mSnappingConfig.
type() ), mSnappingConfig.
tolerance(), mSnappingConfig.
units() );
508 if (
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer ) )
509 layers <<
LayerConfig( vl, _snappingTypeToPointLocatorType( mSnappingConfig.
type() ), mSnappingConfig.
tolerance(), mSnappingConfig.
units() );
519 msg += QString(
"layer : %1\n" 520 "config: %2 tolerance %3 %4\n" )
528 QString extentStr, cachedGeoms, limit( QStringLiteral(
"no max area" ) );
531 extentStr = QStringLiteral(
" extent %1" ).arg( r->toString() );
534 extentStr = QStringLiteral(
"full extent" );
538 cachedGeoms = QStringLiteral(
"not initialized" );
541 if ( mHybridMaxAreaPerLayer.contains( layer.
layer->
id() ) )
543 double maxArea = mStrategy ==
IndexHybrid ? mHybridMaxAreaPerLayer[layer.
layer->
id()] : -1;
545 limit = QStringLiteral(
"max area %1" ).arg( maxArea );
548 limit = QStringLiteral(
"not evaluated" );
550 msg += QStringLiteral(
"index : YES | %1 | %2 | %3\n" ).arg( cachedGeoms, extentStr, limit );
553 msg += QStringLiteral(
"index : ???\n" );
556 msg += QLatin1String(
"index : NO\n" );
557 msg += QLatin1String(
"-\n" );
568 void QgsSnappingUtils::onIndividualLayerSettingsChanged(
const QHash<QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings> &layerSettings )
572 QHash<QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings>::const_iterator i;
574 for ( i = layerSettings.constBegin(); i != layerSettings.constEnd(); ++i )
578 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
Return 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
return 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
return the mode (all layers, active layer, per layer settings)
void setCurrentLayer(QgsVectorLayer *layer)
Set 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
return 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
return 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
Get list of layers for map rendering The layers are stored in the reverse order of how they are rende...
Interface that allows rejection of some matches in intersection queries (e.g.
bool intersectionSnapping() const
return 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 contents of the geometry as a multi linestring if wkbType is WKBMultiLineString, otherwise an empty list.
For all layers build index of full extent. Uses more memory, but queries are faster.
QgsRectangle extent() const
Return geographical coordinates of the rectangle that should be rendered.
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
QString dump()
Get extra information about the instance.
For "big" layers using IndexNeverFull, for the rest IndexAlwaysFull. Compromise between speed and mem...
QgsRectangle extent() const override
Returns the extent of the layer.
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.
double width() const
Returns the width of the rectangle.
QList< QgsSnappingUtils::LayerConfig > layers() const
Query layers used for snapping.
QgsSnappingUtils(QObject *parent=nullptr)
Constructor for QgsSnappingUtils.
Snapped to a vertex. Can be a vertex of the geometry or an intersection.
void edgePoints(QgsPointXY &pt1, QgsPointXY &pt2) const
Only for a valid edge match - obtain endpoints of the edge.
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 null, it will index only that area.
bool hasValidSettings() const
Check whether the map settings are valid and can be used for rendering.
QgsTolerance::UnitType unit
The units in which the tolerance is specified.
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
double tolerance
The range around snapping targets in which snapping should occur.
const QgsRectangle * extent() const
Get extent of the area point locator covers - if null then it caches the whole layer.
QgsSnappingConfig config() const
The snapping configuration controls the behavior of this object.
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
Return 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.
QgsPointLocator * locatorForLayer(QgsVectorLayer *vl)
Get 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.
QgsVectorLayer * layer
The layer to configure.
Configures how a certain layer should be handled in a snapping operation.
bool init(int maxFeaturesToIndex=-1)
Prepare the index for queries.
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...
QgsPointLocator::Types type
To which geometry properties of this layers a snapping should happen.
double distance() const
for vertex / edge match units depending on what class returns it (geom.cache: layer units...
QgsPolylineXY asPolyline() const
Returns contents of the geometry as a polyline if wkbType is WKBLineString, otherwise an empty list...
double tolerance() const
return 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...
QHash< QgsVectorLayer *, QgsSnappingConfig::IndividualLayerSettings > individualLayerSettings() const
return 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.
void setConfig(const QgsSnappingConfig &snappingConfig)
The snapping configuration controls the behavior of this object.
QgsPointXY toMapCoordinates(int x, int y) const
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.