Linked ranges with bokehJS

I have a python script adapted from the documentation to test linked ranges with the option x_range used when the plot is created. It works nicelly.

How to do the same with bokehJS ?
I got an error: Uncaught Error: models must be owned by only a single document

With python

xdr = DataRange1d(bounds=None)
p1 = figure(
        plot_width = 400,
        plot_height = 400,
        tools = "pan,wheel_zoom,box_zoom,reset,save",
        active_scroll = "wheel_zoom",
        x_range = xdr
)

With bokehJS

var xdr = new Bokeh.DataRange1d({bounds: "none"});
var plot = new Bokeh.Plotting.figure({
          plot_width: 400,
          plot_height: 400,
          tools: "pan,wheel_zoom,box_zoom,reset,save",
          //active_scroll:  "wheel_zoom",
          x_range: xdr
        });

@PBrockmann can you share a complete example that reproduces? The problem lies outside this code. You are ending up with the plots in separate Document objects, which means they cannot share a range (or anything else)

Here is the basic example I would like to run.
I expect to have a linked x axis for both plots.

https://jsfiddle.net/PBrockmann/hvmcptke/

This exemple works in python, but I have a lot of interactivity on the browser side so I prefer to work with bokehJS for this specific project.

import numpy as np
from bokeh.models import Legend, DataRange1d
from bokeh.plotting import figure, show, output_file, gridplot

x = np.linspace(0, 4*np.pi, 100)
y = np.sin(x)

output_file("legend_labels.html")

#===================================================
xdr = DataRange1d(bounds=None)

#===================================================
p1 = figure(
        plot_width = 400,
        plot_height = 400,
        tools = "pan,wheel_zoom,box_zoom,reset,save",
        toolbar_location = "above",
        active_scroll = "wheel_zoom",
        x_range = xdr
)

r0 = p1.circle(x, y)
r1 = p1.line(x, y)
r2 = p1.line(x, 2*y, line_dash=[4, 4], line_color="orange", line_width=2)
r3 = p1.square(x, 3*y, fill_color=None, line_color="green")
r4 = p1.line(x, 3*y, line_color="green")

legend = Legend(items=[
    ("sin(x)",   [r0, r1]),
    ("2*sin(x)", [r2]),
    ("3*sin(x)", [r3, r4])
], location=(0,0))

p1.toolbar.logo = None
p1.add_layout(legend, 'right')

#===================================================
p2 = figure(
        plot_width = 400,
        plot_height = 400,
        tools = "pan,wheel_zoom,box_zoom,reset,save",
        toolbar_location = "above",
        active_scroll = "wheel_zoom",
        x_range = xdr
)


r3 = p2.square(x, 3*y, fill_color=None, line_color="green")
r4 = p2.line(x, 3*y, line_color="green")

legend = Legend(items=[
    ("3*sin(x)", [r3, r4])
], location=(0,0))

p2.add_layout(legend, 'right')

#===================================================
p = gridplot([[p1, p2]])

show(p)

This works
https://jsfiddle.net/16p52kLq/2/

Right, the issue is the separate calls to show, which each individually create a new (separate) Document behind the scenes. In Python you are putting everying in a gridplot with one call to show which keeps everything in the same Document. I’m not sure what the options are in the BokehJS API are if you can’t accomplish what you need with one show call. Perhaps @mateusz can comment.

Thanks Xavier for your answer.

Yes indeed but you need to create an array of plot objects at the start.
How do have this feature (linked x_range) when the user will add or remove plots dynamically ?

@PBrockmann The BokehJS API is not very advanced/developed in this regard (standard OSS refrain: lack of resources). You should understand that you are breaking new ground. Looking at the code for show

It always creates a new Document every call. So it is not going to be an option for the kind of usage you describe. Your best bet will probably be to do something like implement your own version of show that can accept an explicit Document instance that you completely control and manage the lifecycle of yourself.

Thank you Bryan for your answer.
I think I understand what should be done.
Synchonization can be painful when plot are dynamically created.

I have a prototype if you want to have a look: http://webportals.ipsl.jussieu.fr/VERIFY/app/v1.4/
The target is to exposed netcdf time series as synchronous plots with possible add/delete specific regions of interest. Add/delete a plot per variable as well.
I think I could recode this prototype with a bokeh/flask approach and be closer to the real bokeh API. But not sure it will solve this specific problem of synchronisation on axis.

For a start one could use a single call to show([p1, p2]) to resolve this issue. However, we could allow multiple calls like proposed here. One option, as mentioned, is to allow explicit Document handling, but that can be tedious. A better solution is to allow show() to check if a model has a document already attached, and then utilize internal logic (build_views()) to make sure we don’t render models multiple times. That should be a pretty straightforward change.

Still interested to be able to instantiate a new document with a model (here an x_range) inherit from a previous document.

I have illustrated what I would like to achieve (2 plots with linked xaxis but created one after the other) from: bokehJS linked on x_range - JSFiddle - Code Playground
I get:
Error: models must be owned by only a single document

I will try to modify bokeh/plotting.ts at branch-3.0 · bokeh/bokeh · GitHub
according to your suggestion.
Just would be nice to know if it is still on the cards.

Is there a way to offer a “bounty” on this feature ?

Is there a way to offer a “bounty” on this feature ?

Dubious. Eg. my own employer would get angry at my taking actual outside money. I suspect others are in similar positions. I still think the best option is to move to explicitly managing a single Document that you could pass in to show (i.e. by modifying show to accept document as an optional parameter instead of unconditionally creating a new Document on every call). Have you tried this?

Speaking my own opinion (@mateusz can comment if he disagrees) I think the idea of allowing models to “live” in multiple documents is a non-starter, since it wholly breaks fundamental assumptions about serializing Bokeh from Python (or other languages). The document is precisely the “envelope” that defines serialization boundaries.