In [ ]:
from __future__ import division

import numpy as np

from bokeh.models import ColumnDataSource, CustomJS, Rect
from bokeh.plotting import output_notebook, figure, show
from bokeh.layouts import row

output_notebook()

In [ ]:
N = 20
img = np.empty((N, N), dtype=np.uint32)
view = img.view(dtype=np.uint8).reshape((N, N, 4))
for i in range(N):
    for j in range(N):
        view[i, j, 0] = int(i/N*255)
        view[i, j, 1] = 158
        view[i, j, 2] = int(j/N*255)
        view[i, j, 3] = 255

In [ ]:
source = ColumnDataSource({'x':[], 'y':[], 'width':[], 'height':[]})

In [ ]:
JSCODE = """
var data = source.data;
var start = cb_obj.start;
var end = cb_obj.end;
data[%r] = [start + (end - start) / 2];
data[%r] = [end - start];

// this is needed because we modified .data "in place"
source.change.emit();
"""

In [ ]:
p1 = figure(title='Box Zoom Here', plot_width=400, plot_height=400,
            x_range=(0,10), y_range=(0,10), tools ='box_zoom,wheel_zoom,pan,reset')
p1.image_rgba(image=[img], x=[0], y=[0], dw=[10], dh=[10])
p1.x_range.callback = CustomJS(args=dict(source=source), code=JSCODE % ('x', 'width'))
p1.y_range.callback = CustomJS(args=dict(source=source), code=JSCODE % ('y', 'height'))

p2 = figure(title='See Zoom Window Here', plot_width=400, plot_height=400, 
            x_range=(0,10), y_range=(0,10), tools="")
p2.image_rgba(image=[img], x=[0], y=[0], dw=[10], dh=[10])
p2.rect('x', 'y', 'width', 'height', fill_alpha=0, line_color='black', source=source)

show(row(p1, p2))

In [ ]:


In [ ]: