bokeh
bokeh copied to clipboard
Allow relative placement of renderers in dynamically sized parents
Problem description
The current way of specifying positions of labels doesn't allow you to position them relative to the borders of dynamically sized figures in other locations than the lower left corner. I've previously brought this issue up in the Discourse forum: https://discourse.bokeh.org/t/placement-of-label-relative-to-figure/9349
It's very simple to create a label that stays put in the lower left corner even when zooming and panning this dynamically sized figure:
from bokeh.plotting import figure, show
from bokeh.models import Label
fig = figure(title='Label position test')
fig.sizing_mode = 'stretch_both'
fig.circle([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], size=20, color="navy", alpha=0.5)
lower_left = Label(x=5, y=5, x_units='screen', y_units='screen', text='Lower left corner')
fig.add_layout(lower_left)
show(fig)
However, if I want to place a label in any of the other corners there's no way for me to do this reliably, because I don't know the size of the figure beforehand.
Feature description
An intuitive way of handling this would be to allow for negative values to offset the labels from the opposite side of the figure. Negative values are supported as coordinates, but they don't function in the way I described:
from bokeh.plotting import figure, show
from bokeh.models import Label
fig = figure(title='Label position test')
fig.sizing_mode = 'stretch_both'
fig.circle([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], size=20, color="navy", alpha=0.5)
lower_left = Label(x=5, y=5, x_units='screen', y_units='screen', text='Lower left corner')
fig.add_layout(lower_left)
# How can I position this relative to the upper bound of the figure?
upper_left = Label(x=5, y=-5, x_units='screen', y_units='screen', text='Upper left corner')
fig.add_layout(upper_left)
show(fig)
Potential alternatives
For the specific example of a label in the upper left corner @bryevdv suggested doing a range callback and updating the y-position of the label whenever the range changes.
Additional information
No response
To be honest I thought negative screen coordinates already behaved this way, but they don't (or don't any more). Are there any use cases to continue interpreting negative screen coordinates literally? If you really want the result above, a zero coordinate with negative offset should work.
I'd propose to have negative screen coordinates be interpreted as position relative to the opposite end of the frame (e.g. analogous to negative array indices). cc @bokeh/dev
I would expect negative screen coordinates to plot outside the frame, if that's possible. Being able to position relative to the other edges is important, but I wouldn't want to give up negative locations to achieve that (if negative locations are currently respected properly).
If you really want the result above, a zero coordinate with negative offset should work.
Ah, ok, yes, I guess that would work. It still seems a bit strange, because positive coordinates that happen to be outside the current frame would draw outside the frame to the right and top while there's no corresponding behavior from the left and bottom. But yes, that would be usable, so I guess ok.
@jbednar Currently the inner frame clips most anything that lands outside of it, labels included. That could be changed for labels, but FWIW I can't recall any time anyone has asked to be able to place labels outside the frame. In any case if we do make that change, what is the best way to allow positioning relative to the opposite axis? Saying that y=5
means two different things depending on some other flag seems pretty awkward and confusing.
Yeah, placing outside the frame is largely theoretical. Where I would imagine using it is when there is no frame border and I'm super-tweaking something for publication, but it's a narrow use case. I don't have any proposed syntax; it would all be quite arbitrary.
PR #12083 adds a symbolic coordinate system that allows relative placement like this. The exact syntax is not yet fully established in that PR at this point, but in the most generic setup, it allows linking position of any node (e.g. a corner) of any renderer with another node (of the same shape) of another renderer, e.g. bottom-left corner of a label with bottom-left corner of the (cartesian) frame, as requested here. This is more robust and generic than what's proposed here, because it doesn't assume whether a potentially negative coordinate would mean positioning top-left to bottom-left or bottom-left to bottom-left (in the proposed case), but allows any combination as needed.
To be honest I thought negative screen coordinates already behaved this way, but they don't (or don't any more).
As far as I can tell, this never worked and was never tested.
Are there any use cases to continue interpreting negative screen coordinates literally?
Yes, since introduction of subcoordinates, screen coordinates (and cartesian frame in PR #12083) are not special anymore. One can apply the same mappings and transformations to screen coordinates as can with any other coordinate systems. The only thing that's special is the final coordinate system of the canvas. In fact, mapping negative values as proposed in this issue, should already be possible with an appropriately defined coordinate mapping. However, without PR #12083, this requires knowledge of widths/height of canvas, frames, etc., which is either inconvenient or impossible to know in a responsive setup.
If you really want the result above, a zero coordinate with negative offset should work.
If this means using -0
, then this is exponentially more finicky than using infinities and nan in other places.
@mattpap if #12083 handles this than that's great. What will the solution to this issue look like in concrete terms once #12083 is in place?
If this means using -0
No, it meant a negative offset
value.