import numpy as np
import pandas as pd
from bokeh.io import output_file, show, vplot
from bokeh._legacy_charts import HeatMap
from bokeh.plotting import figure
from bokeh.models import Range1d

output_file('prevent_user_zooming_or_panning_too_far.html')

## Plot where limits are manually set
x_range = Range1d(0, 3, bound_lower=-1, bound_upper=3.5)
y_range = Range1d(0, 3, bound_lower=-0.5, bound_upper=4)
plot_range = figure(
    tools='pan, wheel_zoom, reset', x_range=x_range, y_range=y_range
)
plot_range.rect(x=[1, 2], y=[1, 1], width=0.9, height=0.9)

## Plot where limits are set automatically by DataRange1d
N = 4000
x = np.random.random(size=N) * 100
y = np.random.random(size=N) * 100
radii = np.random.random(size=N) * 1.5
colors = ["#%02x%02x%02x" % (int(r), int(g), 150) for r, g in zip(50 + 2 * x, 30 + 2 * y)]
plot_datarange = figure(tools='pan, box_zoom, wheel_zoom, reset')
plot_datarange.scatter(x, y, radius=radii, fill_color=colors, fill_alpha=0.6, line_color=None)

## Chart where limits are set on a categorical range (see compared to heatmap in charts)
data = pd.DataFrame(
    {'apples': [4, 5, 8, 12, 4], 'pears': [6, 5, 4, 8, 7], 'bananas': [1, 2, 4, 8, 12]},
    index=['2009', '2010', '2011', '2012', '2013']
)
plot_cat = HeatMap(data)
plot_cat.x_range.bound_lower = 'apples'
plot_cat.x_range.bound_upper = 'pears'
plot_cat.y_range.bound_lower = '2009'
plot_cat.y_range.bound_upper = '2013'

show(vplot(plot_range, plot_datarange, plot_cat))

@@ -1,12 +1,38 @@
+from datetime import date%0A
 import numpy
@@ -58,16 +58,57 @@
 s as pd%0A
+from bokeh._legacy_charts import HeatMap%0A
 from bok
@@ -134,19 +134,19 @@
 _file, s
-how
+ave
 , vplot%0A
@@ -160,37 +160,41 @@
 keh.
-_legacy_charts import HeatMap
+models import Range1d, LinearAxis
 %0Afro
@@ -227,37 +227,48 @@
 gure%0Afrom bokeh.
-model
+sampledata.stock
 s import Range1d
@@ -260,23 +260,20 @@
  import 
-Range1d
+AAPL
 %0A%0Aoutput
@@ -499,21 +499,297 @@
  figure(
-%0A    
+tools='pan, wheel_zoom, reset', x_range=x_range, y_range=y_range)%0Aplot_range.rect(x=%5B1, 2%5D, y=%5B1, 1%5D, width=0.9, height=0.9)%0A%0A## Plot where limits are manually set incorrectly%0Ax_range = Range1d(0, 3)%0Ay_range = Range1d(0, 3, bound_lower=4, bound_upper=-0.5)%0Aplot_range_bad = figure(
 tools='p
@@ -844,17 +844,16 @@
 =y_range
-%0A
 )%0Aplot_r
@@ -848,32 +848,36 @@
 ange)%0Aplot_range
+_bad
 .rect(x=%5B1, 2%5D, 
@@ -1776,12 +1776,624 @@
 3'%0A%0A
-show
+## Plot with multiple ranges%0Aapple_x = np.array(AAPL%5B'date'%5D, dtype=np.datetime64)%0Aapple_y = AAPL%5B'adj_close'%5D%0Ax_range = Range1d(%0A    start=date(2005, 1, 1), end=date(2008, 12, 31),%0A    bound_lower=date(2001, 1, 1), bound_upper=date(2010, 12, 31)%0A)%0Ay_range = Range1d(start=50, end=150, bound_lower=10, bound_upper=190)%0Ay_range_extra = Range1d(start=50, end=150)%0A%0Aplot_extra = figure(x_axis_type=%22datetime%22, x_range=x_range, y_range=y_range)%0Aplot_extra.line(apple_x, apple_y, color='lightblue')%0Aplot_extra.extra_y_ranges = %7B'extra': y_range_extra%7D%0Aplot_extra.add_layout(LinearAxis(y_range_name=%22extra%22), 'left')%0A%0Asave
 (vpl
@@ -2402,24 +2402,40 @@
 (plot_range,
+ plot_range_bad,
  plot_datara
@@ -2447,11 +2447,23 @@
 plot_cat
+, plot_extra
 ))%0A

Update with more examples