Create interactive plot (preserving links between panels) from data dictionary

    data has keys of snr, time, dm, sizes, key and more.
    Optional index arguments are used to filter full data set.
    This can be used to remove bad segments or apply different symbols to subsets.
    url_path is path difference to png files for taptool. ('../plots' for jupyter notebook, 'plots' for public page)
    fileroot is the sdm file name used as root for all png files.

def plotall(data, circleinds=[], crossinds=[], edgeinds=[], htmlname=None, noiseplot=None, url_path='plots', fileroot=None):
    """ Create interactive plot (preserving links between panels) from data dictionary

    data has keys of snr, time, dm, sizes, key and more.
    Optional index arguments are used to filter full data set.
    This can be used to remove bad segments or apply different symbols to subsets.
    url_path is path difference to png files for taptool. ('../plots' for jupyter notebook, 'plots' for public page)
    fileroot is the sdm file name used as root for all png files.
    """

    # set up data dictionary
    if not circleinds: circleinds = calcinds(data, np.abs(data['snrs']).min())
    if not crossinds: crossinds = calcinds(data, -1*np.abs(data['snrs']).min())

    TOOLS = "hover,tap,pan,box_select,wheel_zoom,reset"

    # set ranges
    datalen = len(data['dm'])
    inds = circleinds + crossinds + edgeinds
    dm = [data['dm'][i] for i in inds]
    dm_min = min(min(dm), max(dm)/1.2)
    dm_max = max(max(dm), min(dm)*1.2)
    time = [data['time'][i] for i in inds]
    time_min = min(time)
    time_max = max(time)
    l1 = [data['l1'][i] for i in inds]
    l1_min = min(l1)
    l1_max = max(l1)
    m1 = [data['m1'][i] for i in inds]
    m1_min = min(m1)
    m1_max = max(m1)
    specstd = [data['specstd'][i] for i in inds]
    specstd_min = min(specstd)
    specstd_max = max(specstd)
    imkur = [data['imkur'][i] for i in inds]
    imkur_min = min(imkur)
    imkur_max = max(imkur)

    # create figures
    dmt = Figure(plot_width=950, plot_height=500, toolbar_location="left", x_axis_label='Time (s; relative)',
                 y_axis_label='DM (pc/cm3)', x_range=(time_min, time_max), y_range=(dm_min, dm_max), 
                 output_backend='webgl', tools=TOOLS)
    loc = Figure(plot_width=450, plot_height=400, toolbar_location="left", x_axis_label='l1 (rad)', y_axis_label='m1 (rad)',
                 x_range=(l1_min, l1_max), y_range=(m1_min,m1_max), tools=TOOLS, output_backend='webgl')
    stat = Figure(plot_width=450, plot_height=400, toolbar_location="left", x_axis_label='Spectral std',
                  y_axis_label='Image kurtosis', x_range=(specstd_min, specstd_max), 
                  y_range=(imkur_min, imkur_max), tools=TOOLS, output_backend='webgl')
    norm = Figure(plot_width=450, plot_height=400, toolbar_location="left", x_axis_label='SNR observed',
                  y_axis_label='SNR expected', tools=TOOLS, output_backend='webgl')

    # create positive symbol source and add glyphs
    source = ColumnDataSource(data = dict({(key, tuple([value[i] for i in circleinds if i not in edgeinds])) 
                                           for (key, value) in data.iteritems()}))
    dmt.circle('time', 'dm', size='sizes', fill_color='colors', line_color=None, fill_alpha=0.2, source=source)
    loc.circle('l1', 'm1', size='sizes', line_color=None, fill_color='colors', fill_alpha=0.2, source=source)
    stat.circle('specstd', 'imkur', size='sizes', line_color=None, fill_color='colors', fill_alpha=0.2, source=source)
    norm.circle('abssnr', 'zs', size='sizes', line_color=None, fill_color='colors', fill_alpha=0.2, source=source)

    # create negative symbol source and add glyphs
    if crossinds:
        sourceneg = ColumnDataSource(data = dict({(key, tuple([value[i] for i in crossinds]))
                                                  for (key, value) in data.iteritems()}))
        dmt.cross('time', 'dm', size='sizes', fill_color='colors', line_alpha=0.3, source=sourceneg)
        loc.cross('l1', 'm1', size='sizes', line_color='colors', line_alpha=0.3, source=sourceneg)
        stat.cross('specstd', 'imkur', size='sizes', line_color='colors', line_alpha=0.3, source=sourceneg)
        norm.cross('abssnr', 'zs', size='sizes', line_color='colors', line_alpha=0.3, source=sourceneg)

    # create linked symbol source and add glyphs
    if edgeinds:
        sourceedge = ColumnDataSource(data = dict({(key, tuple([value[i] for i in edgeinds]))
                                                   for (key, value) in data.iteritems()}))
        dmt.circle('time', 'dm', size='sizes', line_color='colors', fill_color='colors', line_alpha=0.5, fill_alpha=0.2, source=sourceedge)
        loc.circle('l1', 'm1', size='sizes', line_color='colors', fill_color='colors', source=sourceedge, line_alpha=0.5, fill_alpha=0.2)
        stat.circle('specstd', 'imkur', size='sizes', line_color='colors', fill_color='colors', source=sourceedge, line_alpha=0.5, fill_alpha=0.2)
        norm.circle('abssnr', 'zs', size='sizes', line_color='colors', fill_color='colors', source=sourceedge, line_alpha=0.5, fill_alpha=0.2)

    hover = dmt.select(dict(type=HoverTool))
    hover.tooltips = OrderedDict([('SNR', '@snrs'), ('key', '@key')])
    hover = loc.select(dict(type=HoverTool))
    hover.tooltips = OrderedDict([('SNR', '@snrs'), ('key', '@key')])
    hover = stat.select(dict(type=HoverTool))
    hover.tooltips = OrderedDict([('SNR', '@snrs'), ('key', '@key')])
    hover = norm.select(dict(type=HoverTool))
    hover.tooltips = OrderedDict([('SNR', '@snrs'), ('key', '@key')])

    if url_path and fileroot:
        url = '{}/cands_{}_@key.png'.format(url_path, fileroot)
        taptool = dmt.select(type=TapTool)
        taptool.callback = OpenURL(url=url)
        taptool = loc.select(type=TapTool)
        taptool.callback = OpenURL(url=url)
        taptool = stat.select(type=TapTool)
        taptool.callback = OpenURL(url=url)
        taptool = norm.select(type=TapTool)
        taptool.callback = OpenURL(url=url)

# this approach does not preserve links between panels
#    dmt = plotdmt(data, circleinds=circleinds, crossinds=crossinds, edgeinds=edgeinds, url_path=url_path, fileroot=fileroot, tools=TOOLS) # maybe add size?
#    loc = plotloc(data, circleinds=circleinds, crossinds=crossinds, edgeinds=edgeinds, url_path=url_path, fileroot=fileroot, tools=TOOLS)
#    stat = plotstat(data, circleinds=circleinds, crossinds=crossinds, edgeinds=edgeinds, url_path=url_path, fileroot=fileroot, tools=TOOLS)
#    norm = plotnorm(data, circleinds=circleinds, crossinds=crossinds, edgeinds=edgeinds, url_path=url_path, fileroot=fileroot, tools=TOOLS)

    # arrange figures
    top = Row(dmt, width=950)
    middle = Row(loc, stat, width=950)
    if noiseplot:
        bottom = Row(norm, noiseplot, width=950)
    else:
        bottom = Row(norm, width=950)
    combined = Column(top, middle, bottom, width=950)

    if htmlname:
        output_file(htmlname)
        save(combined)
    else:
        return combined