Request for Comments : symbol clipping¶
Request for QGIS core modification : add the ability to clip symbol layers by the shape of labels or symbol markers.
The goal is to be able to draw labels where some symbol layers underneath can be cut by a space surrounding labels' shape.
Here is an example :
The « Wollishofen » label interacts with some choosen underlying layers (some white and black road lines here). Within a distance around the label, these layers are not drawn.
The objective is to ease the overall readability of labels.
It is close to the « buffer » function that is available for labels, except that a buffer mask underlying layers with an opaque color. Here, some underlying layers must not be drawn.
Symbols that are not drawn when such a feature is enabled can be chosen at a symbol layer level : some symbol layer of a layer may be drawn and some not.
The shape used around labels to « cut » underlying layers are defined by their « buffer » and « background » properties.
The functionality must also be available for symbol markers, not only for labels.
Example :
To achieve this functionality, the existing symbol layer and rendering part of QGIS may involve important changes.
This is then subject to acceptance by the community and design choices will have to be discussed.
GUI design proposals¶
There are different things to define, from a user point of view :- what shape to use as a clipping shape
- for what labels / symbols
- and what symbol layers are affected by these clipping shapes
In order to define the clipping shapes, it seems adding some widgets to the styling properties make sense.
For label clipping, we may want to reuse the existing "buffer" and "background" options to define a clipping shape.- Could we have a label that is surrounded by an opaque (white) buffer and, at the same time, by a clipping shape a bit larger that would cut some symbol layers underneath ? If yes, then we need to add new options for that.
For point symbols, similar options could be added to the styling properties, but probably at the same level as general rendering properties (transparency of the layer, blending modes).
In order to define symbol layers that are affected by clipping shapes, the "symbol levels" window could be somehow reused. The user could then select which symbol levels would be clipped by this labels/points.
Core design proposal¶
Possible designs, from a technical point of view :
Rendering part¶
Since the shape of labels cannot be determined before all the vector layers have been rendered, the clipping region of a layer cannot be determined before all the vector layers on top of it have been rendered.
This leads to implementing this feature with a two-pass rendering : stack painting orders used in layer renderers before sending them to the Qpainter pipeline. This way, some painting orders could be kept partially determined (clipping path of a layer) until the end of the rendering.
This would decompose the rendering process in two : within the first pass, each layer would be accessed as it is now, fetching geometries of features and applying symbology, eventually based on QgsExpressions. But rather than sending painting orders directly to the Qpainter pipeline, these « orders » would be stacked to another data structure where some orders could be partly-defined. In order for the final to user to be able to interact with the rendering (and especialy abort it in a multithreaded environment), painting orders would be sent constinuously to the QT rendering pipeline.
Possible partly-defined orders could be : « use a clip path for features of this symbol layer where the exact path will be given by the shape of features of the layer #N / of labels». If asked for an output to a Qpainter, such orders will fallback to a default behaviour, like a null clipping path. The complete rendering would then be refined until the end.
To minimize the impact of such modifications on the existing code, we propose to implement the stacking of painting orders as a special QpaintEngine that would proxy every order to a « concrete » QPaintEngine