mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
Support different indexing strategies in QgsSnappingUtils
Huge layers will not use all the memory - at the expense of slow queries
This commit is contained in:
parent
54e8493bc7
commit
0ea6a3d15f
@ -17,7 +17,6 @@ class QgsSnappingUtils : QObject
|
||||
/** snap to map according to the current configuration (mode). Optional filter allows to discard unwanted matches. */
|
||||
QgsPointLocator::Match snapToMap( const QPoint& point, QgsPointLocator::MatchFilter* filter = 0 );
|
||||
QgsPointLocator::Match snapToMap( const QgsPoint& pointMap, QgsPointLocator::MatchFilter* filter = 0 );
|
||||
// TODO: multi-variant
|
||||
|
||||
/** snap to current layer */
|
||||
QgsPointLocator::Match snapToCurrentLayer( const QPoint& point, int type, QgsPointLocator::MatchFilter* filter = 0 );
|
||||
@ -47,6 +46,18 @@ class QgsSnappingUtils : QObject
|
||||
/** Find out how the snapping to map is done */
|
||||
SnapToMapMode snapToMapMode() const;
|
||||
|
||||
enum IndexingStrategy
|
||||
{
|
||||
IndexAlwaysFull, //!< For all layers build index of full extent. Uses more memory, but queries are faster.
|
||||
IndexNeverFull, //!< For all layers only create temporary indexes of small extent. Low memory usage, slower queries.
|
||||
IndexHybrid //!< For "big" layers using IndexNeverFull, for the rest IndexAlwaysFull. Compromise between speed and memory usage.
|
||||
};
|
||||
|
||||
/** Set a strategy for indexing geometry data - determines how fast and memory consuming the data structures will be */
|
||||
void setIndexingStrategy( IndexingStrategy strategy );
|
||||
/** Find out which strategy is used for indexing - by default hybrid indexing is used */
|
||||
IndexingStrategy indexingStrategy() const;
|
||||
|
||||
/** configure options used when the mode is snap to current layer */
|
||||
void setDefaultSettings( int type, double tolerance, QgsTolerance::UnitType unit );
|
||||
/** query options used when the mode is snap to current layer */
|
||||
|
@ -25,6 +25,7 @@ QgsSnappingUtils::QgsSnappingUtils( QObject* parent )
|
||||
: QObject( parent )
|
||||
, mCurrentLayer( 0 )
|
||||
, mSnapToMapMode( SnapCurrentLayer )
|
||||
, mStrategy( IndexHybrid )
|
||||
, mDefaultType( QgsPointLocator::Vertex )
|
||||
, mDefaultTolerance( 10 )
|
||||
, mDefaultUnit( QgsTolerance::Pixels )
|
||||
@ -57,6 +58,38 @@ void QgsSnappingUtils::clearAllLocators()
|
||||
foreach ( QgsPointLocator* vlpl, mLocators )
|
||||
delete vlpl;
|
||||
mLocators.clear();
|
||||
|
||||
foreach ( QgsPointLocator* vlpl, mTemporaryLocators )
|
||||
delete vlpl;
|
||||
mTemporaryLocators.clear();
|
||||
}
|
||||
|
||||
|
||||
QgsPointLocator* QgsSnappingUtils::locatorForLayerUsingStrategy( QgsVectorLayer* vl, const QgsPoint& pointMap, double tolerance )
|
||||
{
|
||||
if ( mStrategy == IndexAlwaysFull )
|
||||
return locatorForLayer( vl );
|
||||
else if ( mStrategy == IndexNeverFull )
|
||||
return temporaryLocatorForLayer( vl, pointMap, tolerance );
|
||||
else // Hybrid
|
||||
{
|
||||
if ( vl->pendingFeatureCount() > 100000 )
|
||||
return temporaryLocatorForLayer( vl, pointMap, tolerance );
|
||||
else
|
||||
return locatorForLayer( vl );
|
||||
}
|
||||
}
|
||||
|
||||
QgsPointLocator* QgsSnappingUtils::temporaryLocatorForLayer( QgsVectorLayer* vl, const QgsPoint& pointMap, double tolerance )
|
||||
{
|
||||
if ( mTemporaryLocators.contains( vl ) )
|
||||
delete mTemporaryLocators.take( vl );
|
||||
|
||||
QgsRectangle rect( pointMap.x() - tolerance, pointMap.y() - tolerance,
|
||||
pointMap.x() + tolerance, pointMap.y() + tolerance );
|
||||
QgsPointLocator* vlpl = new QgsPointLocator( vl, destCRS(), &rect );
|
||||
mTemporaryLocators.insert( vl, vlpl );
|
||||
return mTemporaryLocators.value( vl );
|
||||
}
|
||||
|
||||
|
||||
@ -174,17 +207,18 @@ QgsPointLocator::Match QgsSnappingUtils::snapToMap( const QgsPoint& pointMap, Qg
|
||||
int type = mDefaultType;
|
||||
|
||||
// use ad-hoc locator
|
||||
QgsPointLocator* loc = locatorForLayer( mCurrentLayer );
|
||||
loc->init( QgsPointLocator::Vertex | QgsPointLocator::Edge );
|
||||
QgsPointLocator* loc = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
|
||||
if ( !loc )
|
||||
return QgsPointLocator::Match();
|
||||
loc->init( QgsPointLocator::Vertex | QgsPointLocator::Edge );
|
||||
|
||||
QgsPointLocator::Match bestMatch;
|
||||
_updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter );
|
||||
|
||||
if ( mSnapOnIntersection )
|
||||
{
|
||||
QgsPointLocator::MatchList edges = locatorForLayer( mCurrentLayer )->edgesInTolerance( pointMap, tolerance );
|
||||
QgsPointLocator* locEdges = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
|
||||
QgsPointLocator::MatchList edges = locEdges->edgesInTolerance( pointMap, tolerance );
|
||||
bestMatch.replaceIfBetter( _findClosestSegmentIntersection( pointMap, edges ), tolerance );
|
||||
}
|
||||
|
||||
@ -199,7 +233,7 @@ QgsPointLocator::Match QgsSnappingUtils::snapToMap( const QgsPoint& pointMap, Qg
|
||||
foreach ( const LayerConfig& layerConfig, mLayers )
|
||||
{
|
||||
double tolerance = QgsTolerance::toleranceInMapUnits( layerConfig.tolerance, mMapSettings, layerConfig.unit );
|
||||
if ( QgsPointLocator* loc = locatorForLayer( layerConfig.layer ) )
|
||||
if ( QgsPointLocator* loc = locatorForLayerUsingStrategy( layerConfig.layer, pointMap, tolerance ) )
|
||||
{
|
||||
loc->init( layerConfig.type );
|
||||
|
||||
@ -228,14 +262,14 @@ QgsPointLocator::Match QgsSnappingUtils::snapToCurrentLayer( const QPoint& point
|
||||
if ( !mCurrentLayer )
|
||||
return QgsPointLocator::Match();
|
||||
|
||||
QgsPointLocator* loc = locatorForLayer( mCurrentLayer );
|
||||
loc->init( type );
|
||||
if ( !loc )
|
||||
return QgsPointLocator::Match();
|
||||
|
||||
QgsPoint pointMap = mMapSettings.mapToPixel().toMapCoordinates( point );
|
||||
double tolerance = QgsTolerance::vertexSearchRadius( mMapSettings );
|
||||
|
||||
QgsPointLocator* loc = locatorForLayerUsingStrategy( mCurrentLayer, pointMap, tolerance );
|
||||
if ( !loc )
|
||||
return QgsPointLocator::Match();
|
||||
loc->init( type );
|
||||
|
||||
QgsPointLocator::Match bestMatch;
|
||||
_updateBestMatch( bestMatch, pointMap, loc, type, tolerance, filter );
|
||||
return bestMatch;
|
||||
@ -356,6 +390,15 @@ void QgsSnappingUtils::onLayersWillBeRemoved( QStringList layerIds )
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for ( LocatorsMap::const_iterator it = mTemporaryLocators.constBegin(); it != mTemporaryLocators.constEnd(); ++it )
|
||||
{
|
||||
if ( it.key()->id() == layerId )
|
||||
{
|
||||
delete mTemporaryLocators.take( it.key() );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,8 +22,19 @@
|
||||
#include "qgspointlocator.h"
|
||||
|
||||
/**
|
||||
* Has all the configuration of snapping and can return answers to snapping queries.
|
||||
* This one will be also available from iface for map tools.
|
||||
* This class has all the configuration of snapping and can return answers to snapping queries.
|
||||
* Internally, it keeps a cache of QgsPointLocator instances for multiple layers.
|
||||
*
|
||||
* Currently it supports the following queries:
|
||||
* - snapToMap() - has multiple modes of operation
|
||||
* - snapToCurrentLayer()
|
||||
* For more complex queries it is possible to use locatorForLayer() method that returns
|
||||
* point locator instance with layer's indexed data.
|
||||
*
|
||||
* Indexing strategy determines how fast the queries will be and how much memory will be used.
|
||||
*
|
||||
* When working with map canvas, it may be useful to use derived class QgsMapCanvasSnappingUtils
|
||||
* which keeps the configuration in sync with map canvas (e.g. current view, active layer).
|
||||
*
|
||||
* @note added in 2.8
|
||||
*/
|
||||
@ -42,7 +53,6 @@ class QgsSnappingUtils : public QObject
|
||||
/** snap to map according to the current configuration (mode). Optional filter allows to discard unwanted matches. */
|
||||
QgsPointLocator::Match snapToMap( const QPoint& point, QgsPointLocator::MatchFilter* filter = 0 );
|
||||
QgsPointLocator::Match snapToMap( const QgsPoint& pointMap, QgsPointLocator::MatchFilter* filter = 0 );
|
||||
// TODO: multi-variant
|
||||
|
||||
/** snap to current layer */
|
||||
QgsPointLocator::Match snapToCurrentLayer( const QPoint& point, int type, QgsPointLocator::MatchFilter* filter = 0 );
|
||||
@ -72,6 +82,18 @@ class QgsSnappingUtils : public QObject
|
||||
/** Find out how the snapping to map is done */
|
||||
SnapToMapMode snapToMapMode() const { return mSnapToMapMode; }
|
||||
|
||||
enum IndexingStrategy
|
||||
{
|
||||
IndexAlwaysFull, //!< For all layers build index of full extent. Uses more memory, but queries are faster.
|
||||
IndexNeverFull, //!< For all layers only create temporary indexes of small extent. Low memory usage, slower queries.
|
||||
IndexHybrid //!< For "big" layers using IndexNeverFull, for the rest IndexAlwaysFull. Compromise between speed and memory usage.
|
||||
};
|
||||
|
||||
/** Set a strategy for indexing geometry data - determines how fast and memory consuming the data structures will be */
|
||||
void setIndexingStrategy( IndexingStrategy strategy ) { mStrategy = strategy; }
|
||||
/** Find out which strategy is used for indexing - by default hybrid indexing is used */
|
||||
IndexingStrategy indexingStrategy() const { return mStrategy; }
|
||||
|
||||
/** configure options used when the mode is snap to current layer */
|
||||
void setDefaultSettings( int type, double tolerance, QgsTolerance::UnitType unit );
|
||||
/** query options used when the mode is snap to current layer */
|
||||
@ -97,21 +119,10 @@ class QgsSnappingUtils : public QObject
|
||||
/** Query whether to consider intersections of nearby segments for snapping */
|
||||
bool snapOnIntersections() const { return mSnapOnIntersection; }
|
||||
|
||||
#if 0
|
||||
/** Set topological editing status (used by some map tools) */
|
||||
void setTopologicalEditing( bool enabled );
|
||||
/** Query topological editing status (used by some map tools) */
|
||||
bool topologicalEditing() const;
|
||||
#endif
|
||||
|
||||
public slots:
|
||||
/** Read snapping configuration from the project */
|
||||
void readConfigFromProject();
|
||||
|
||||
// requirements:
|
||||
// - support existing configurations
|
||||
// - handle updates from QgsProject::setSnapSettingsForLayer()
|
||||
|
||||
private slots:
|
||||
void onLayersWillBeRemoved( QStringList layerIds );
|
||||
|
||||
@ -122,6 +133,11 @@ class QgsSnappingUtils : public QObject
|
||||
//! delete all existing locators (e.g. when destination CRS has changed and we need to reindex)
|
||||
void clearAllLocators();
|
||||
|
||||
//! return a locator (temporary or not) according to the indexing strategy
|
||||
QgsPointLocator* locatorForLayerUsingStrategy( QgsVectorLayer* vl, const QgsPoint& pointMap, double tolerance );
|
||||
//! return a temporary locator with index only for a small area (will be replaced by another one on next request)
|
||||
QgsPointLocator* temporaryLocatorForLayer( QgsVectorLayer* vl, const QgsPoint& pointMap, double tolerance );
|
||||
|
||||
private:
|
||||
// environment
|
||||
QgsMapSettings mMapSettings;
|
||||
@ -129,6 +145,7 @@ class QgsSnappingUtils : public QObject
|
||||
|
||||
// configuration
|
||||
SnapToMapMode mSnapToMapMode;
|
||||
IndexingStrategy mStrategy;
|
||||
int mDefaultType;
|
||||
double mDefaultTolerance;
|
||||
QgsTolerance::UnitType mDefaultUnit;
|
||||
@ -139,6 +156,8 @@ class QgsSnappingUtils : public QObject
|
||||
typedef QMap<QgsVectorLayer*, QgsPointLocator*> LocatorsMap;
|
||||
//! on-demand locators used (locators are owned)
|
||||
LocatorsMap mLocators;
|
||||
//! temporary locators (indexing just a part of layers). owned by the instance
|
||||
LocatorsMap mTemporaryLocators;
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user