Plot range is reset when ColumnDataSource is updated and drawn using Ray

I have an issue when trying to link several plots and at the same time to draw cross at mouse position (also on all the plots). The linking is done using js callback, because I need to link different types of ranges (like datetime with numerical, or two numericals with offset). The hover cross is done by drawing rays and updating ColumnDataSource in js callback.

What happens here is that when I try to pan/zoom the first plot before moving the second, it’s stuck, but once the second plot is moved it starts to work as it should. The linking only works fine, as well as only crosses, but not both at the same time.

I’ve made a small example to reproduce the issue:

from datetime import datetime, timedelta
import bokeh
from bokeh.layouts import column
from bokeh.plotting import figure, show
from bokeh.io import output_file
from bokeh.model import Model
from bokeh.models import HoverTool, Range, CustomJS, ColumnDataSource
output_file(‘axis-link-bug.html’)

first = figure(x_axis_type=‘datetime’)
second = figure(x_axis_type=‘datetime’)

first.line([datetime.now(), datetime.now()+timedelta(seconds=100000)], [0, 100])
second.line([datetime.now(), datetime.now()+timedelta(seconds=100000)], [0, 100])

first_source = ColumnDataSource(data=dict(x=[0], y=[0], angle=[90]))
second_source = ColumnDataSource(data=dict(x=[0], y=[0], angle=[90]))

first.ray(x=‘x’, y=‘y’, length=0, angle=‘angle’, angle_units=“deg”,
color=‘grey’, line_width=1, source=first_source)

second.ray(x=‘x’, y=‘y’, length=0, angle=‘angle’, angle_units=“deg”,
color=‘grey’, line_width=1, source=second_source)

hover_js = ‘’’
var geometry = cb_data[‘geometry’];
first_source.data[‘x’][0] = geometry.x;
second_source.data[‘x’][0] = geometry.x;
first_source.change.emit();
second_source.change.emit();
‘’’

hover = HoverTool(tooltips=None,
callback=CustomJS(code=hover_js, args={‘first_source’: first_source,
‘second_source’: second_source}))
first.add_tools(hover)
second.add_tools(hover)

class RangeLinker(Model):
ranges = List(Instance(Range))
implementation = “”"
import * as p from “core/properties”
import {Model} from “model”

export class RangeLinker extends Model

type: ‘RangeLinker’

@define {
ranges: [ p.Array, ]
}

handler: (rangeIndex) =>
return (() => @sync(rangeIndex))

initialize: (attrs, options) =>
super(attrs, options)

for r, i in @ranges
  @connect(r.change, @handler(i))

@sync(0)

sync: (rangeIndex) =>
range = @ranges[rangeIndex]
for r, i in @ranges
if (i != rangeIndex)
r.attributes[‘start’] = range.start
r.attributes[‘end’] = range.end
if (r.plots? and r.plots.length > 0)
r.plots[0].change.emit()
“”"

time_link = RangeLinker(ranges=[first.x_range, second.x_range])
numeric_link = RangeLinker(ranges=[first.y_range, second.y_range])

first.x_range.callback = CustomJS(args=dict(time_link=time_link))
first.y_range.callback = CustomJS(args=dict(numeric_link=numeric_link))

show(column(first, second))

``

I’m sorry, forgot to tell the environment. I’m using Bokeh 0.12.16, Python 3.6.3, browsing using Firefox 60.0.1 on Ubuntu 16.04.

Hi,

By default the defat DataRange objects auto-scale over all available glyph renderers. Since you are using Raw more like an annotation, this is probably not idea. You can configure the ranges .renderers property to only have the lines, and not the rays, e.g. similar to:

  r = p.line(...)
        p.circle(...)
  plot.x_range.renderers = [r] # only autorange the line

Thanks,

Bryan

···

On Jun 7, 2018, at 01:17, [email protected] wrote:

I have an issue when trying to link several plots and at the same time to draw cross at mouse position (also on all the plots). The linking is done using js callback, because I need to link different types of ranges (like datetime with numerical, or two numericals with offset). The hover cross is done by drawing rays and updating ColumnDataSource in js callback.

What happens here is that when I try to pan/zoom the first plot before moving the second, it's stuck, but once the second plot is moved it starts to work as it should. The linking only works fine, as well as only crosses, but not both at the same time.

I've made a small example to reproduce the issue:
from datetime import datetime, timedelta
import bokeh
from bokeh.layouts import column
from bokeh.plotting import figure, show
from bokeh.io import output_file
from bokeh.model import Model
from bokeh.models import HoverTool, Range, CustomJS, ColumnDataSource
output_file('axis-link-bug.html')

first = figure(x_axis_type='datetime')
second = figure(x_axis_type='datetime')

first.line([datetime.now(), datetime.now()+timedelta(seconds=100000)], [0, 100])
second.line([datetime.now(), datetime.now()+timedelta(seconds=100000)], [0, 100])

first_source = ColumnDataSource(data=dict(x=[0], y=[0], angle=[90]))
second_source = ColumnDataSource(data=dict(x=[0], y=[0], angle=[90]))

first.ray(x='x', y='y', length=0, angle='angle', angle_units="deg",
          color='grey', line_width=1, source=first_source)

second.ray(x='x', y='y', length=0, angle='angle', angle_units="deg",
           color='grey', line_width=1, source=second_source)

hover_js = '''
var geometry = cb_data['geometry'];
first_source.data['x'][0] = geometry.x;
second_source.data['x'][0] = geometry.x;
first_source.change.emit();
second_source.change.emit();
'''

hover = HoverTool(tooltips=None,
                  callback=CustomJS(code=hover_js, args={'first_source': first_source,
                                                         'second_source': second_source}))
first.add_tools(hover)
second.add_tools(hover)

class RangeLinker(Model):
    ranges = List(Instance(Range))
    __implementation__ = """
import * as p from "core/properties"
import {Model} from "model"

export class RangeLinker extends Model

  type: 'RangeLinker'

  @define {
    ranges: [ p.Array, ]
  }

  handler: (rangeIndex) =>
    return (() => @sync(rangeIndex))

  initialize: (attrs, options) =>
    super(attrs, options)

    for r, i in @ranges
      @connect(r.change, @handler(i))

    @sync(0)

  sync: (rangeIndex) =>
    range = @ranges[rangeIndex]
    for r, i in @ranges
      if (i != rangeIndex)
        r.attributes['start'] = range.start
        r.attributes['end'] = range.end
        if (r.plots? and r.plots.length > 0)
          r.plots[0].change.emit()
"""

time_link = RangeLinker(ranges=[first.x_range, second.x_range])
numeric_link = RangeLinker(ranges=[first.y_range, second.y_range])

first.x_range.callback = CustomJS(args=dict(time_link=time_link))
first.y_range.callback = CustomJS(args=dict(numeric_link=numeric_link))

show(column(first, second))

--
You received this message because you are subscribed to the Google Groups "Bokeh Discussion - Public" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/b70bc09f-4ce6-4eac-8589-0f40c97ae9f2%40continuum.io\.
For more options, visit https://groups.google.com/a/continuum.io/d/optout\.

Hi,

I’ve tried both:

first.x_range.renderers =
second.x_range.renderers =

``

and

first.x_range.renderers = [first_line]
second.x_range.renderers = [second_line]

``

but none of these helped.

···

пятница, 15 июня 2018 г., 18:52:40 UTC+3 пользователь Bryan Van de ven написал:

Hi,

By default the defat DataRange objects auto-scale over all available glyph renderers. Since you are using Raw more like an annotation, this is probably not idea. You can configure the ranges .renderers property to only have the lines, and not the rays, e.g. similar to:

    r = p.line(...)

    p.circle(...)

    plot.x_range.renderers = [r] # only autorange the line

Thanks,

Bryan

On Jun 7, 2018, at 01:17, [email protected] wrote:

I have an issue when trying to link several plots and at the same time to draw cross at mouse position (also on all the plots). The linking is done using js callback, because I need to link different types of ranges (like datetime with numerical, or two numericals with offset). The hover cross is done by drawing rays and updating ColumnDataSource in js callback.

What happens here is that when I try to pan/zoom the first plot before moving the second, it’s stuck, but once the second plot is moved it starts to work as it should. The linking only works fine, as well as only crosses, but not both at the same time.

I’ve made a small example to reproduce the issue:

from datetime import datetime, timedelta

import bokeh

from bokeh.layouts import column

from bokeh.plotting import figure, show

from bokeh.io import output_file

from bokeh.model import Model

from bokeh.models import HoverTool, Range, CustomJS, ColumnDataSource

output_file(‘axis-link-bug.html’)

first = figure(x_axis_type=‘datetime’)

second = figure(x_axis_type=‘datetime’)

first.line([datetime.now(), datetime.now()+timedelta(seconds=100000)], [0, 100])

second.line([datetime.now(), datetime.now()+timedelta(seconds=100000)], [0, 100])

first_source = ColumnDataSource(data=dict(x=[0], y=[0], angle=[90]))

second_source = ColumnDataSource(data=dict(x=[0], y=[0], angle=[90]))

first.ray(x=‘x’, y=‘y’, length=0, angle=‘angle’, angle_units=“deg”,

      color='grey', line_width=1, source=first_source)

second.ray(x=‘x’, y=‘y’, length=0, angle=‘angle’, angle_units=“deg”,

       color='grey', line_width=1, source=second_source)

hover_js = ‘’’

var geometry = cb_data[‘geometry’];

first_source.data[‘x’][0] = geometry.x;

second_source.data[‘x’][0] = geometry.x;

first_source.change.emit();

second_source.change.emit();

‘’’

hover = HoverTool(tooltips=None,

              callback=CustomJS(code=hover_js, args={'first_source': first_source,
                                                     'second_source': second_source}))

first.add_tools(hover)

second.add_tools(hover)

class RangeLinker(Model):

ranges = List(Instance(Range))
__implementation__ = """

import * as p from “core/properties”

import {Model} from “model”

export class RangeLinker extends Model

type: ‘RangeLinker’

@define {

ranges:           [ p.Array, [] ]

}

handler: (rangeIndex) =>

return (() => @sync(rangeIndex))

initialize: (attrs, options) =>

super(attrs, options)
for r, i in @ranges
  @connect(r.change, @handler(i))
@sync(0)

sync: (rangeIndex) =>

range = @ranges[rangeIndex]
for r, i in @ranges
  if (i != rangeIndex)
    r.attributes['start'] = range.start
    r.attributes['end'] = range.end
    if (r.plots? and r.plots.length > 0)
      r.plots[0].change.emit()

“”"

time_link = RangeLinker(ranges=[first.x_range, second.x_range])

numeric_link = RangeLinker(ranges=[first.y_range, second.y_range])

first.x_range.callback = CustomJS(args=dict(time_link=time_link))

first.y_range.callback = CustomJS(args=dict(numeric_link=numeric_link))

show(column(first, second))


You received this message because you are subscribed to the Google Groups “Bokeh Discussion - Public” group.

To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].

To post to this group, send email to [email protected].

To view this discussion on the web visit https://groups.google.com/a/continuum.io/d/msgid/bokeh/b70bc09f-4ce6-4eac-8589-0f40c97ae9f2%40continuum.io.

For more options, visit https://groups.google.com/a/continuum.io/d/optout.