-
-
Notifications
You must be signed in to change notification settings - Fork 3k
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
Expose snapping indexing strategy [FEATURE] #8862
Conversation
This adds a combo box in the snapping widget that allows to choose the snapping indexing strategy. When snapping indexes get detroyed frequently (by declaring "data dependencies" between layers for example), it becomes useful to tune the strategy used to build snap indexes.
QDomAttr attr = snappingNode.toElement().attributeNode( QStringLiteral( "strategy" ) ); | ||
if ( !attr.isNull() ) | ||
{ | ||
mSnappingUtils->setIndexingStrategy( static_cast<QgsSnappingUtils::IndexingStrategy>( attr.value().toInt() ) ); |
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.
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.
Ah I did not know, I'll have a look, thanks
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.
mSnappingUtils->setIndexingStrategy( static_cast<QgsSnappingUtils::IndexingStrategy>( attr.value().toInt() ) ); | |
mSnappingUtils->setIndexingStrategy( attr.value().value<QgsSnappingUtils::IndexingStrategy>() ); |
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.
indeed, but it was in QgsSettings only. I will move this in qgis.h so it could be used anywhere.
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.
integrated in #8879
Sounds like a good proposal to me. CC @wonder-sk |
Just playing devils advocate here -- is this something we really want to expose to users? Is there no way we can tune the automatic method selection to handle this use case without user intervention? |
@nyalldawson To be honest, I am also still a bit hesitating to expose this kind of implementation detail to users. So, thanks for your concern :) This feature is related to the problem I face with database layers that have geometry triggers: http://osgeo-org.1560.x6.nabble.com/QGIS-Developer-Vertex-tool-and-cache-invalidation-td5389381.html I found that using the IndexExtent strategy and dataDependencies declared on layers allows to have the new vertex tool usable with layers that have side effects from database triggers. The default strategy makes it barely usable because snapping indexes get rebuilt with each edition and it is very slow. This is not a perfect situation, since a lot of work have to be done by the user just to have a usable snapping (add "data dependencies", and now switch to "IndexExtent") and should probably be seen as a temporary solution waiting for a less "intrusive" one (if that only exists). But I agree adding things to the UI make them not really "temporary" ... |
@@ -412,6 +433,16 @@ void QgsSnappingWidget::changeUnit( int idx ) | |||
updateToleranceDecimals(); | |||
} | |||
|
|||
void QgsSnappingWidget::changeStrategy( int idx ) | |||
{ | |||
QgsSnappingUtils::IndexingStrategy strategy = static_cast<QgsSnappingUtils::IndexingStrategy>( mStrategyComboBox->itemData( idx ).toInt() ); |
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.
QgsSnappingUtils::IndexingStrategy strategy = static_cast<QgsSnappingUtils::IndexingStrategy>( mStrategyComboBox->itemData( idx ).toInt() ); | |
QgsSnappingUtils::IndexingStrategy strategy = mStrategyComboBox->itemData( idx ).value<QgsSnappingUtils::IndexingStrategy>(); |
I am also -0 on adding this to the configuration, this is a bit obscure. |
If this is the case, could we just make IndexExtent automatically set whenever a layer has dataDependencies set? |
For what it's worth, I'd be in favor of avoiding further cluttering the UI here in favor of coming up with an improved logic / fix too. |
... and/or trigger cache invalidation when a dataDependency indicates a change? |
I get your point, but ... there may be projects where a few data dependencies work also well with the default indexing strategy but does not work well with IndexExtent. It is hard to tell.
This is already what happens (and is the only real usage of dataDependency AFAIK): it emits "dataChanged" whenever a feature is added/changed/deleted on a dependent layer. And dataChanged generates an index destruction (in QgsPointLocator). @m-kuhn @nyalldawson @nirvn |
Ah nice, thanks!
As it's hard to tell: could you check with the customer if this changed heuristic works for him and then we decide if it really needs to be an option? |
If this is a project-level setting (as currently implemented?) we'd be better finding it a place in the project properties. |
I've tested a bit more the different strategies in the context of a project with layers that have lots of dependencies between them:
There are also situations where people get bad performances and do not understand why (https://issues.qgis.org/issues/20843) All of this tells me the default 'IndexHybrid', like any heuristics, cannot fits all situations and the heuristic choice should be changeable for "advanced" use cases.
I would also prefer a project-level setting. |
Please please... can we keep the indexing strategy as a little secret away for the poor users? :-) The indexing strategy is a very low level implementation detail and it was never meant to be exposed to users... hybrid strategy should be the answer for everyone. If the current hybrid strategy is not good enough, I am sure that we can come up with some improved heuristics to make it better. I am worried that by exposing indexing strategy we do not really solve the issues some people may be having - things need to work out of the box. Some thoughts I had earlier on how to improve indexing: snapping could have two modes - strict and relaxed. Right now we only have strict mode, so all answers from the API must be correct. The new relaxed mode (which would be the default) would allow some requests to be answered temporarily incorrectly, but the plus side it would have better UX, because re-indexing could be done in background thread (not blocking GUI like now) and the "old" locators would not need to be destroyed immediately, just marked as needing update. Like this, any updates (including data dependencies / triggers) should not disrupt the user experience and everything should feel snappy... even without having to choose indexing strategy.
QgsSnappingUtils::dump() gives some pointers on what is going on, so if needed this debugging information could be queried (e.g. in python console) - not sure if we need a GUI for that...? |
Sorry, I'm late to the party again :) My dream - as a user - would be to have no cache at all, and no additional option to control snapping behavior. I tend to agree with @wonder-sk here. However, I'm not sure I really get how the 'relaxed mode' would look like. You make me curious Martin! |
I can see in @wonder-sk 's mind |
@3nids pretty much that's how I thought about it :-) @haubourg to elaborate a bit on what I mean... Right now the main problem with UX with snapping is in situations when a snap at a map point is requested - and the implementation realizes that the cache is not present or cannot satisfy the request. In such case, the cache gets built and that may lock up the GUI for a bit. That's because the cache is built in the main thread (which also did the snap request). Once the cache is updated, the snap request is (quickly) processed and GUI is unlocked again. This wait time may be unpleasant but necessary to make sure that we return correct response to the snap request. My thinking about this "relaxed" mode is the fact that not all the time we need to get the perfect answer for the snap requests. So the "relaxed" snap request would check the caches and give the result immediately without extra wait - even if some caches are not ready. At the same time it would start cache update in a background thread to make sure that caches are ready in full power sometime very soon. This way we "risk" that users would not see some snaps when they first arrive with their cursor on the desired position, but maybe a second later the snap would start to appear and mainly the GUI would not get locked. This is just an idea I thought about before - maybe there are better solutions so I am keen to hear others' opinions. But the main point stays - users should not be required to tweak some obscure settings to get good results. |
@wonder-sk sounds like a nice plan, especially if users are shown some feedback to alert them that the index is being updated. @m-hugo Given the concerns raised here, can we close this PR, or do something like rework to be a hidden custom property on the layer so that you can satisfy your immediate needs without exposing anything to users? |
@wonder-sk Hi. Thanks for your feedback. About this "relaxed" mode. Indexing things using multiple thread may of course speed up things, which is a good thing and probably reduce the need to tweak the indexing strategy in most cases. However, I am not sure about the second part where you say we will get wrong results immediately, followed by snap fixes. If I understand correctly, it will give the very strange results we'd like to avoid where the snapping index is out of date and other tools are aware of the new geometry. As it is the case right now with the vertex tool for instance: Or am I missing something ? While you are here, I have two questions :)
Sure ! I've closed. Anyway we can continue discussing questions raised here on a closed PR :) |
This is an independent problem I would say - what you mention is a case when we don't even know that snapping index is out of date and needs refreshing. That is something that providers would need to signal.
The other strategies were intended to be used for anyone using snapping API directly without a map canvas. If I need fast queries in a layer, I can construct my own QgsSnappingUtils class and have a better control of how the index behaves. For usage in map canvas the hybrid (heuristic) approach should be fine (not saying that it is perfect now). So I would not deprecate it.
We used to have that before with old QgsSnapper implementation - its cache would get populated in rendering loop. I did not like such approach for various reasons...
|
Sorry I did not get a notification for your reply
Hmmm. Let's suppose we have a reworked indexing code following your proposition where the indexing takes place in a separated thread and with a "relaxed" mode where the user can still plays on the canvas in the meantime.
Indeed, that could be another solution. But it seems to me quite complex and quite intrusive "just" to overcome a cache problem (especially for the postgres provider). This is why I am still trying to investigate other possibilities.
Sure. My question was: do you know if it is actually used outside of a mapcanvas ?
Thanks for your answer. Some points that I don't understand:
Why ? If the snapping index follows the rendering loop, why should it be much larger ? Why not refreshing the index when moving the map ?
You mean for applications outside of a mapcanvas I guess ?
Yes I totally agree on these two points, it may adds a lot of complexity. |
@wonder-sk ^ (not sure if we get notifications on a closed PR) |
This adds a combo box in the snapping widget that allows to choose the
snapping indexing strategy.
When snapping indexes get destroyed frequently (by declaring "data
dependencies" between layers for example), it becomes useful to tune
the strategy used to build snap indexes.
Description
Include a few sentences describing the overall goals for this PR (pull request). If applicable also add screenshots.
Checklist
fixes #11111
in the commit message next to the description[FEATURE]
in the commit message[needs-docs]
in the commit message and contain sufficient information in the commit message to be documentedscripts/prepare-commit.sh
script before each commit