Bokehのプロットエリアをウィンドウのサイズに合わせる

こんにちは。
[Bokeh](https://bokeh.pydata.org/) のプロットエリアをウィンドウのサイズに合わせる（リサイズにも追従させてぴったりなエリアサイズへ自動的に変更する）実現方法を調べてみました。

* "[How to fit bokeh plot size dynamically](https://stackoverflow.com/questions/48879674/how-to-fit-bokeh-plot-size-dynamically)" (Stack Overflow)

```console
$ ./bokeh_resize.py 
writing a file: bokeh_resize.html
```

<img width="551" alt="bokeh_resize.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/54617/d79eb300-0c4c-d70b-5f9c-76f236e83806.jpeg">

```bokeh_resize.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ref: "How to fit bokeh plot size dynamically" (https://stackoverflow.com/questions/48879674/how-to-fit-bokeh-plot-size-dynamically)

def main():
    import io, sys, random
    aspectRatio = 1
    ndata = 10 * 1000
    filename = 'bokeh_resize.html'
    datax = [random.random()*200 for r in range(ndata)]
    datay = [random.random()*100 for r in range(ndata)]
    color = ["#%02x%02x%02x" % (random.randrange(128, 256), random.randrange(128, 192), random.randrange(128, 192)) for i in range(ndata)]
    html = plotData(datax, datay, color, aspectRatio)
    print(f'writing a file: {filename}', file=sys.stderr)
    with io.open(filename, mode='w', encoding='utf-8') as f:
        f.write(html)
    return

def plotData(datax, datay, color, aspectRatio=None):
    from jinja2 import Template
    from bokeh.embed import components
    from bokeh.plotting import figure
    from bokeh.resources import INLINE
    from bokeh.util.browser import view
    from bokeh.models import WheelZoomTool

    SCATTER_OPTIONS = dict(size=3, marker="circle", alpha=0.8, fill_color=color, line_color=None)
    PLOT_SIZE_OPTIONS = dict(plot_width=1200, plot_height=400)
    PLOT_OPTIONS = dict(sizing_mode='scale_width', tools="pan,wheel_zoom,reset", id="plt_fig", output_backend="webgl")
    ASPECT_OPTIONS = {} if aspectRatio is None else dict(match_aspect=True, aspect_scale=aspectRatio)
    plt = figure(**PLOT_OPTIONS, **ASPECT_OPTIONS, **PLOT_SIZE_OPTIONS)
    plt.scatter(datax, datay, **SCATTER_OPTIONS)
    plt.toolbar.active_scroll = plt.select_one(WheelZoomTool)
    
    # Define our html template for out plots
    template = Template('''<!DOCTYPE html>
    <head>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> 
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.4/TweenMax.min.js"></script>
    <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
    {{ js_resources }}
    {{ css_resources }}
    </head>
    <body>
    <div id="panel">   
        <div class="col-md-6" id="card1">
            <div class="card">            
                    <div class="row">
                        <div class="col-sm-12">resizable plot area to fit the width of a window</div>
                    </div>        
                    <div id="div1">
                    {{ div1 }}
                    </div>
            </div>
        </div>
    </div>
    <script>
    (function($){
       var po = $('#panel').offset();
       var co = $('#card1').offset();   
       var x=0, y=po.top-co.top;  
            TweenMax.to($("#card1"), 0.5, {x:x, y:y, className:"col-md-12"});
            TweenMax.to($("#div1"), 0.5, {onComplete:function(){Bokeh.index["plt_fig"].resize();}});
          $(window).resize();
          $(window).trigger('resize');     
    })(jQuery);
    </script>
    </body>
    </html>
    ''')
    
    resources = INLINE
    js_resources = resources.render_js()
    css_resources = resources.render_css()
    script, div = components(plt)
    div1 = div + script
    html = template.render(js_resources=js_resources,
                           css_resources=css_resources,
                           div1=div1)
    return html

if __name__ == '__main__':
    main()
```