Skip to content

Commit

Permalink
Merge pull request #6750 from lbartoletti/visibleSnapPerf
Browse files Browse the repository at this point in the history
[BUGFIX][FEATURE][NEEDS-DOCS] Disable snapping on invisible features. Second version
  • Loading branch information
wonder-sk committed May 1, 2018
2 parents cec59c0 + 9f3d571 commit 3b36cdd
Show file tree
Hide file tree
Showing 14 changed files with 364 additions and 122 deletions.
8 changes: 7 additions & 1 deletion python/core/qgspointlocator.sip.in
Expand Up @@ -71,6 +71,13 @@ Get extent of the area point locator covers - if null then it caches the whole l
Configure extent - if not null, it will index only that area

.. versionadded:: 2.14
%End

void setRenderContext( const QgsRenderContext *context );
%Docstring
Configure render context - if not null, it will use to index only visible feature

.. versionadded:: 3.2
%End

enum Type
Expand Down Expand Up @@ -199,7 +206,6 @@ Override of edgesInRect that construct rectangle from a center point and toleran
find out if the point is in any polygons
%End


int cachedGeometryCount() const;
%Docstring
Return how many geometries are cached in the index
Expand Down
11 changes: 10 additions & 1 deletion python/core/qgssnappingutils.sip.in
Expand Up @@ -35,7 +35,7 @@ which keeps the configuration in sync with map canvas (e.g. current view, active
%End
public:

QgsSnappingUtils( QObject *parent /TransferThis/ = 0 );
QgsSnappingUtils( QObject *parent /TransferThis/ = 0, bool enableSnappingForInvisibleFeature = true );
%Docstring
Constructor for QgsSnappingUtils
%End
Expand Down Expand Up @@ -139,6 +139,15 @@ Get extra information about the instance
QgsSnappingConfig config() const;
%Docstring
The snapping configuration controls the behavior of this object
%End

void setEnableSnappingForInvisibleFeature( bool enable );
%Docstring
Set if invisible features must be snapped or not.

:param enable: Enable or not this feature

.. versionadded:: 3.2
%End

public slots:
Expand Down
1 change: 1 addition & 0 deletions python/gui/qgsmapcanvassnappingutils.sip.in
Expand Up @@ -9,6 +9,7 @@




class QgsMapCanvasSnappingUtils : QgsSnappingUtils
{
%Docstring
Expand Down
3 changes: 3 additions & 0 deletions resources/qgis_global_settings.ini
Expand Up @@ -20,6 +20,9 @@ digitizing\default_snap_type=1
# 2 = Project units
digitizing\default_snapping_tolerance_unit=1

# Snap on invisble feature
digitizing\snap_invisible_feature=false

# Default XYZ tile servers to include
connections-xyz\OpenStreetMap\authcfg=
connections-xyz\OpenStreetMap\password=
Expand Down
2 changes: 2 additions & 0 deletions src/app/qgsoptions.cpp
Expand Up @@ -974,6 +974,7 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl, const QList<QgsOpti

mSnappingMarkerColorButton->setColor( mSettings->value( QStringLiteral( "/qgis/digitizing/snap_color" ), QColor( Qt::magenta ) ).value<QColor>() );
mSnappingTooltipsCheckbox->setChecked( mSettings->value( QStringLiteral( "/qgis/digitizing/snap_tooltip" ), false ).toBool() );
mEnableSnappingOnInvisibleFeatureCheckbox->setChecked( mSettings->value( QStringLiteral( "/qgis/digitizing/snap_invisible_feature" ), false ).toBool() );

//vertex marker
mMarkersOnlyForSelectedCheckBox->setChecked( mSettings->value( QStringLiteral( "/qgis/digitizing/marker_only_for_selected" ), true ).toBool() );
Expand Down Expand Up @@ -1488,6 +1489,7 @@ void QgsOptions::saveOptions()

mSettings->setValue( QStringLiteral( "/qgis/digitizing/snap_color" ), mSnappingMarkerColorButton->color() );
mSettings->setValue( QStringLiteral( "/qgis/digitizing/snap_tooltip" ), mSnappingTooltipsCheckbox->isChecked() );
mSettings->setValue( QStringLiteral( "/qgis/digitizing/snap_invisible_feature" ), mEnableSnappingOnInvisibleFeatureCheckbox->isChecked() );

mSettings->setValue( QStringLiteral( "/qgis/digitizing/marker_only_for_selected" ), mMarkersOnlyForSelectedCheckBox->isChecked() );

Expand Down
1 change: 1 addition & 0 deletions src/core/layertree/qgslayertreemodellegendnode.cpp
Expand Up @@ -351,6 +351,7 @@ bool QgsSymbolLegendNode::setData( const QVariant &value, int role )
vlayer->renderer()->checkLegendSymbolItem( mItem.ruleKey(), value == Qt::Checked );

emit dataChanged();
vlayer->emitStyleChanged();

vlayer->triggerRepaint();

Expand Down
87 changes: 87 additions & 0 deletions src/core/qgspointlocator.cpp
Expand Up @@ -21,6 +21,7 @@
#include "qgswkbptr.h"
#include "qgis.h"
#include "qgslogger.h"
#include "qgsrenderer.h"

#include <SpatialIndex.h>

Expand Down Expand Up @@ -635,6 +636,7 @@ QgsPointLocator::QgsPointLocator( QgsVectorLayer *layer, const QgsCoordinateRefe
connect( mLayer, &QgsVectorLayer::featureAdded, this, &QgsPointLocator::onFeatureAdded );
connect( mLayer, &QgsVectorLayer::featureDeleted, this, &QgsPointLocator::onFeatureDeleted );
connect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsPointLocator::onGeometryChanged );
connect( mLayer, &QgsVectorLayer::attributeValueChanged, this, &QgsPointLocator::onAttributeValueChanged );
connect( mLayer, &QgsVectorLayer::dataChanged, this, &QgsPointLocator::destroyIndex );
}

Expand All @@ -661,6 +663,20 @@ void QgsPointLocator::setExtent( const QgsRectangle *extent )
destroyIndex();
}

void QgsPointLocator::setRenderContext( const QgsRenderContext *context )
{
disconnect( mLayer, &QgsVectorLayer::styleChanged, this, &QgsPointLocator::destroyIndex );

destroyIndex();
mContext.reset( nullptr );

if ( context )
{
mContext = std::unique_ptr<QgsRenderContext>( new QgsRenderContext( *context ) );
connect( mLayer, &QgsVectorLayer::styleChanged, this, &QgsPointLocator::destroyIndex );
}

}

bool QgsPointLocator::init( int maxFeaturesToIndex )
{
Expand All @@ -686,6 +702,7 @@ bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex )

QgsFeatureRequest request;
request.setSubsetOfAttributes( QgsAttributeList() );

if ( mExtent )
{
QgsRectangle rect = *mExtent;
Expand All @@ -704,13 +721,40 @@ bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex )
}
request.setFilterRect( rect );
}

bool filter = false;
std::unique_ptr< QgsFeatureRenderer > renderer( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr );
QgsRenderContext *ctx = nullptr;
if ( mContext )
{
mContext->expressionContext() << QgsExpressionContextUtils::layerScope( mLayer );
ctx = mContext.get();
if ( renderer )
{
// setup scale for scale dependent visibility (rule based)
renderer->startRender( *ctx, mLayer->fields() );
filter = renderer->capabilities() & QgsFeatureRenderer::Filter;
request.setSubsetOfAttributes( renderer->usedAttributes( *ctx ), mLayer->fields() );
}
}

QgsFeatureIterator fi = mLayer->getFeatures( request );
int indexedCount = 0;

while ( fi.nextFeature( f ) )
{
if ( !f.hasGeometry() )
continue;

if ( filter && ctx && renderer )
{
ctx->expressionContext().setFeature( f );
if ( !renderer->willRenderFeature( f, *ctx ) )
{
continue;
}
}

if ( mTransform.isValid() )
{
try
Expand Down Expand Up @@ -761,6 +805,12 @@ bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex )
QgsPointLocator_Stream stream( dataList );
mRTree = RTree::createAndBulkLoadNewRTree( RTree::BLM_STR, stream, *mStorage, fillFactor, indexCapacity,
leafCapacity, dimension, variant, indexId );

if ( ctx && renderer )
{
renderer->stopRender( *ctx );
renderer.release();
}
return true;
}

Expand Down Expand Up @@ -792,6 +842,31 @@ void QgsPointLocator::onFeatureAdded( QgsFeatureId fid )
if ( !f.hasGeometry() )
return;

if ( mContext )
{
std::unique_ptr< QgsFeatureRenderer > renderer( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr );
QgsRenderContext *ctx = nullptr;

mContext->expressionContext() << QgsExpressionContextUtils::layerScope( mLayer );
ctx = mContext.get();
if ( renderer && ctx )
{
bool pass = false;
renderer->startRender( *ctx, mLayer->fields() );

ctx->expressionContext().setFeature( f );
if ( !renderer->willRenderFeature( f, *ctx ) )
{
pass = true;
}

renderer->stopRender( *ctx );
renderer.release();
if ( pass )
return;
}
}

if ( mTransform.isValid() )
{
try
Expand Down Expand Up @@ -832,6 +907,7 @@ void QgsPointLocator::onFeatureDeleted( QgsFeatureId fid )
mRTree->deleteData( rect2region( mGeoms[fid]->boundingBox() ), fid );
delete mGeoms.take( fid );
}

}

void QgsPointLocator::onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geom )
Expand All @@ -841,6 +917,17 @@ void QgsPointLocator::onGeometryChanged( QgsFeatureId fid, const QgsGeometry &ge
onFeatureAdded( fid );
}

void QgsPointLocator::onAttributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value )
{
Q_UNUSED( idx );
Q_UNUSED( value );
if ( mContext )
{
onFeatureDeleted( fid );
onFeatureAdded( fid );
}
}


QgsPointLocator::Match QgsPointLocator::nearestVertex( const QgsPointXY &point, double tolerance, MatchFilter *filter )
{
Expand Down
15 changes: 13 additions & 2 deletions src/core/qgspointlocator.h
Expand Up @@ -18,12 +18,15 @@

class QgsPointXY;
class QgsVectorLayer;
class QgsFeatureRenderer;
class QgsRenderContext;

#include "qgis_core.h"
#include "qgsfeature.h"
#include "qgspointxy.h"
#include "qgscoordinatereferencesystem.h"
#include "qgscoordinatetransform.h"
#include <memory>

class QgsPointLocator_VisitorNearestVertex;
class QgsPointLocator_VisitorNearestEdge;
Expand Down Expand Up @@ -92,6 +95,12 @@ class CORE_EXPORT QgsPointLocator : public QObject
*/
void setExtent( const QgsRectangle *extent );

/**
* Configure render context - if not null, it will use to index only visible feature
* \since QGIS 3.2
*/
void setRenderContext( const QgsRenderContext *context );

/**
* The type of a snap result or the filter type for a snap request.
*/
Expand Down Expand Up @@ -251,8 +260,6 @@ class CORE_EXPORT QgsPointLocator : public QObject
//! find out if the point is in any polygons
MatchList pointInPolygon( const QgsPointXY &point );

//

/**
* Return how many geometries are cached in the index
* \since QGIS 2.14
Expand All @@ -267,6 +274,7 @@ class CORE_EXPORT QgsPointLocator : public QObject
void onFeatureAdded( QgsFeatureId fid );
void onFeatureDeleted( QgsFeatureId fid );
void onGeometryChanged( QgsFeatureId fid, const QgsGeometry &geom );
void onAttributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value );

private:
//! Storage manager
Expand All @@ -278,11 +286,14 @@ class CORE_EXPORT QgsPointLocator : public QObject
//! flag whether the layer is currently empty (i.e. mRTree is null but it is not necessary to rebuild it)
bool mIsEmptyLayer;


//! R-tree containing spatial index
QgsCoordinateTransform mTransform;
QgsVectorLayer *mLayer = nullptr;
QgsRectangle *mExtent = nullptr;

std::unique_ptr<QgsRenderContext> mContext;

friend class QgsPointLocator_VisitorNearestVertex;
friend class QgsPointLocator_VisitorNearestEdge;
friend class QgsPointLocator_VisitorArea;
Expand Down
23 changes: 18 additions & 5 deletions src/core/qgssnappingutils.cpp
Expand Up @@ -19,10 +19,12 @@
#include "qgsproject.h"
#include "qgsvectorlayer.h"
#include "qgslogger.h"
#include "qgsrenderer.h"

QgsSnappingUtils::QgsSnappingUtils( QObject *parent )
QgsSnappingUtils::QgsSnappingUtils( QObject *parent, bool enableSnappingForInvisibleFeature )
: QObject( parent )
, mSnappingConfig( QgsProject::instance() )
, mEnableSnappingForInvisibleFeature( enableSnappingForInvisibleFeature )
{
}

Expand Down Expand Up @@ -92,7 +94,6 @@ bool QgsSnappingUtils::isIndexPrepared( QgsVectorLayer *vl, const QgsRectangle &
return ( mStrategy == IndexHybrid || mStrategy == IndexExtent ) && loc->hasIndex() && ( !loc->extent() || loc->extent()->contains( aoi ) ); // the index - even if it exists - is not suitable
}


static QgsPointLocator::Match _findClosestSegmentIntersection( const QgsPointXY &pt, const QgsPointLocator::MatchList &segments )
{
if ( segments.isEmpty() )
Expand Down Expand Up @@ -156,9 +157,9 @@ static QgsPointLocator::Match _findClosestSegmentIntersection( const QgsPointXY
return QgsPointLocator::Match( QgsPointLocator::Vertex, nullptr, 0, std::sqrt( minSqrDist ), minP );
}


static void _replaceIfBetter( QgsPointLocator::Match &bestMatch, const QgsPointLocator::Match &candidateMatch, double maxDistance )
{

// is candidate match relevant?
if ( !candidateMatch.isValid() || candidateMatch.distance() > maxDistance )
return;
Expand All @@ -174,7 +175,6 @@ static void _replaceIfBetter( QgsPointLocator::Match &bestMatch, const QgsPointL
bestMatch = candidateMatch; // the other match is better!
}


static void _updateBestMatch( QgsPointLocator::Match &bestMatch, const QgsPointXY &pointMap, QgsPointLocator *loc, QgsPointLocator::Types type, double tolerance, QgsPointLocator::MatchFilter *filter )
{
if ( type & QgsPointLocator::Vertex )
Expand Down Expand Up @@ -329,7 +329,6 @@ QgsPointLocator::Match QgsSnappingUtils::snapToMap( const QgsPointXY &pointMap,
return QgsPointLocator::Match();
}


void QgsSnappingUtils::prepareIndex( const QList<LayerAndAreaOfInterest> &layers )
{
if ( mIsIndexing )
Expand All @@ -341,6 +340,7 @@ void QgsSnappingUtils::prepareIndex( const QList<LayerAndAreaOfInterest> &layers
Q_FOREACH ( const LayerAndAreaOfInterest &entry, layers )
{
QgsVectorLayer *vl = entry.first;

if ( vl->geometryType() == QgsWkbTypes::NullGeometry || mStrategy == IndexNeverFull )
continue;

Expand All @@ -359,7 +359,15 @@ void QgsSnappingUtils::prepareIndex( const QList<LayerAndAreaOfInterest> &layers
QgsVectorLayer *vl = entry.first;
QTime tt;
tt.start();

QgsPointLocator *loc = locatorForLayer( vl );

if ( !mEnableSnappingForInvisibleFeature )
{
QgsRenderContext ctx = QgsRenderContext::fromMapSettings( mMapSettings );
loc->setRenderContext( &ctx );
}

if ( mStrategy == IndexExtent )
{
QgsRectangle rect( mMapSettings.extent() );
Expand Down Expand Up @@ -428,6 +436,11 @@ QgsSnappingConfig QgsSnappingUtils::config() const
return mSnappingConfig;
}

void QgsSnappingUtils::setEnableSnappingForInvisibleFeature( bool enable )
{
mEnableSnappingForInvisibleFeature = enable;
}

void QgsSnappingUtils::setConfig( const QgsSnappingConfig &config )
{
if ( mSnappingConfig == config )
Expand Down

0 comments on commit 3b36cdd

Please sign in to comment.