-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BUGFIX][FEATURE][NEEDS-DOCS] Disable snapping on invisible features. Second version #6750
Changes from 4 commits
66b0e59
5e5fdf9
43e905d
07caafe
68f463e
9f3d571
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ | |
|
||
|
||
|
||
|
||
class QgsMapCanvasSnappingUtils : QgsSnappingUtils | ||
{ | ||
%Docstring | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,7 @@ | |
#include "qgswkbptr.h" | ||
#include "qgis.h" | ||
#include "qgslogger.h" | ||
#include "qgsrenderer.h" | ||
|
||
#include <SpatialIndex.h> | ||
|
||
|
@@ -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 ); | ||
} | ||
|
||
|
@@ -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 ) | ||
{ | ||
|
@@ -686,6 +702,7 @@ bool QgsPointLocator::rebuildIndex( int maxFeaturesToIndex ) | |
|
||
QgsFeatureRequest request; | ||
request.setSubsetOfAttributes( QgsAttributeList() ); | ||
|
||
if ( mExtent ) | ||
{ | ||
QgsRectangle rect = *mExtent; | ||
|
@@ -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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Couldn't you just use mContext directly instead of ctx? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No sorry, it doesn't work :/ |
||
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. set request's filter expression here to renderer->filter() There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, if I do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's why in this branch you should set the subset of attributes to those required by the renderer (renderer->usedAttributes()) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh yes. Thank you! |
||
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 | ||
|
@@ -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; | ||
} | ||
|
||
|
@@ -792,6 +842,30 @@ void QgsPointLocator::onFeatureAdded( QgsFeatureId fid ) | |
if ( !f.hasGeometry() ) | ||
return; | ||
|
||
std::unique_ptr< QgsFeatureRenderer > renderer( mLayer->renderer() ? mLayer->renderer()->clone() : nullptr ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be better to be cloned inside "if ( mContext )" block, so we do not copy it when snapping also to invisible features. |
||
QgsRenderContext *ctx = nullptr; | ||
if ( mContext ) | ||
{ | ||
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 | ||
|
@@ -832,6 +906,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 ) | ||
|
@@ -841,6 +916,14 @@ 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 ); | ||
onFeatureDeleted( fid ); | ||
onFeatureAdded( fid ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These two functions should be called only when snapping to visible features... |
||
} | ||
|
||
|
||
QgsPointLocator::Match QgsPointLocator::nearestVertex( const QgsPointXY &point, double tolerance, MatchFilter *filter ) | ||
{ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to me there is no way to turn off visibility-dependent behavior of point locator after it has been enabled?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is possible to configure the behavior in the preferences and then rebuild the index
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's necessary to destroy the index, if you change the style of your layer, since the subject is especially to filter invisible feature.
Initially there was a bug when you checked a "sub style". I added possible cases.
In use, only styleChanged is really needed. What do you think if I do this:
remove:
connect( mLayer, &QgsVectorLayer::rendererChanged, this, &QgsPointLocator::destroyIndex ); connect( mLayer, &QgsVectorLayer::styleChanged, this, &QgsPointLocator::destroyIndex ); connect( mLayer, &QgsVectorLayer::layerModified, this, &QgsPointLocator::destroyIndex );
add
connect( mLayer, &QgsVectorLayer::styleChanged, this, &QgsPointLocator::destroyIndex );
inQgsPointLocator::setRenderContext