示例#1
0
    def test_js_on_change_executes(self, bokeh_model_page):
        slider = Slider(start=0, end=10, value=1, title="bar", css_classes=["foo"], width=300)
        slider.js_on_change('value', CustomJS(code=RECORD("value", "cb_obj.value")))

        page = bokeh_model_page(slider)

        drag_slider(page.driver, ".foo", 150)

        results = page.results
        assert float(results['value']) > 1

        assert page.has_no_console_errors()
示例#2
0
def slider():
    x = np.linspace(0, 10, 100)
    y = np.sin(x)

    source = ColumnDataSource(data=dict(x=x, y=y))

    plot = figure(
        y_range=(-10, 10), tools='', toolbar_location=None,
        title="Sliders example")
    plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)

    amp_slider = Slider(start=0.1, end=10, value=1, step=.1, title="Amplitude")
    freq_slider = Slider(start=0.1, end=10, value=1, step=.1, title="Frequency")
    phase_slider = Slider(start=0, end=6.4, value=0, step=.1, title="Phase")
    offset_slider = Slider(start=-5, end=5, value=0, step=.1, title="Offset")

    callback = CustomJS(args=dict(source=source, amp=amp_slider, freq=freq_slider, phase=phase_slider, offset=offset_slider),
                        code="""
        const data = source.data;
        const A = amp.value;
        const k = freq.value;
        const phi = phase.value;
        const B = offset.value;
        const x = data['x']
        const y = data['y']
        for (var i = 0; i < x.length; i++) {
            y[i] = B + A*Math.sin(k*x[i]+phi);
        }
        source.change.emit();
    """)

    amp_slider.js_on_change('value', callback)
    freq_slider.js_on_change('value', callback)
    phase_slider.js_on_change('value', callback)
    offset_slider.js_on_change('value', callback)

    widgets = column(amp_slider, freq_slider, phase_slider, offset_slider)
    return [widgets, plot]
示例#3
0
def photometry_plot(obj_id, user, width=600, height=300):
    """Create scatter plot of photometry for object.
    Parameters
    ----------
    obj_id : str
        ID of Obj to be plotted.
    Returns
    -------
    (str, str)
        Returns (docs_json, render_items) json for the desired plot.
    """

    data = pd.read_sql(
        DBSession().query(
            Photometry,
            Telescope.nickname.label("telescope"),
            Instrument.name.label("instrument"),
        ).join(Instrument, Instrument.id == Photometry.instrument_id).join(
            Telescope, Telescope.id == Instrument.telescope_id).filter(
                Photometry.obj_id == obj_id).filter(
                    Photometry.groups.any(
                        Group.id.in_([g.id for g in user.groups]))).statement,
        DBSession().bind)

    if data.empty:
        return None, None, None

    data['color'] = [get_color(f) for f in data['filter']]
    data['label'] = [
        f'{i} {f}-band' for i, f in zip(data['instrument'], data['filter'])
    ]

    data['zp'] = PHOT_ZP
    data['magsys'] = 'ab'
    data['alpha'] = 1.
    data['lim_mag'] = -2.5 * np.log10(
        data['fluxerr'] * DETECT_THRESH) + data['zp']

    # Passing a dictionary to a bokeh datasource causes the frontend to die,
    # deleting the dictionary column fixes that
    del data['original_user_data']

    # keep track of things that are only upper limits
    data['hasflux'] = ~data['flux'].isna()

    # calculate the magnitudes - a photometry point is considered "significant"
    # or "detected" (and thus can be represented by a magnitude) if its snr
    # is above DETECT_THRESH
    obsind = data['hasflux'] & (data['flux'].fillna(0.) / data['fluxerr'] >=
                                DETECT_THRESH)
    data.loc[~obsind, 'mag'] = None
    data.loc[obsind, 'mag'] = -2.5 * np.log10(data[obsind]['flux']) + PHOT_ZP

    # calculate the magnitude errors using standard error propagation formulae
    # https://en.wikipedia.org/wiki/Propagation_of_uncertainty#Example_formulae
    data.loc[~obsind, 'magerr'] = None
    coeff = 2.5 / np.log(10)
    magerrs = np.abs(coeff * data[obsind]['fluxerr'] / data[obsind]['flux'])
    data.loc[obsind, 'magerr'] = magerrs
    data['obs'] = obsind
    data['stacked'] = False

    split = data.groupby('label', sort=False)

    # show middle 98% of data
    finite = np.isfinite(data['flux'])
    fdata = data[finite]
    lower = np.percentile(fdata['flux'], 1.)
    upper = np.percentile(fdata['flux'], 99.)

    lower -= np.abs(lower) * 0.1
    upper += np.abs(upper) * 0.1

    plot = figure(plot_width=width,
                  plot_height=height,
                  active_drag='box_zoom',
                  tools='box_zoom,wheel_zoom,pan,reset,save',
                  y_range=(lower, upper))

    imhover = HoverTool(tooltips=tooltip_format)
    plot.add_tools(imhover)

    model_dict = {}

    for i, (label, sdf) in enumerate(split):

        # for the flux plot, we only show things that have a flux value
        df = sdf[sdf['hasflux']]

        key = f'obs{i}'
        model_dict[key] = plot.scatter(
            x='mjd',
            y='flux',
            color='color',
            marker='circle',
            fill_color='color',
            alpha='alpha',
            source=ColumnDataSource(df),
        )

        imhover.renderers.append(model_dict[key])

        key = f'bin{i}'
        model_dict[key] = plot.scatter(
            x='mjd',
            y='flux',
            color='color',
            marker='circle',
            fill_color='color',
            source=ColumnDataSource(data=dict(mjd=[],
                                              flux=[],
                                              fluxerr=[],
                                              filter=[],
                                              color=[],
                                              lim_mag=[],
                                              mag=[],
                                              magerr=[],
                                              stacked=[],
                                              instrument=[])))

        imhover.renderers.append(model_dict[key])

        key = 'obserr' + str(i)
        y_err_x = []
        y_err_y = []

        for d, ro in df.iterrows():
            px = ro['mjd']
            py = ro['flux']
            err = ro['fluxerr']

            y_err_x.append((px, px))
            y_err_y.append((py - err, py + err))

        model_dict[key] = plot.multi_line(
            xs='xs',
            ys='ys',
            color='color',
            alpha='alpha',
            source=ColumnDataSource(data=dict(xs=y_err_x,
                                              ys=y_err_y,
                                              color=df['color'],
                                              alpha=[1.] * len(df))))

        key = f'binerr{i}'
        model_dict[key] = plot.multi_line(
            xs='xs',
            ys='ys',
            color='color',
            source=ColumnDataSource(data=dict(xs=[], ys=[], color=[])))

    plot.xaxis.axis_label = 'MJD'
    plot.yaxis.axis_label = 'Flux (μJy)'
    plot.toolbar.logo = None

    toggle = CheckboxWithLegendGroup(labels=list(data.label.unique()),
                                     active=list(
                                         range(len(data.label.unique()))),
                                     colors=list(data.color.unique()))

    # TODO replace `eval` with Namespaces
    # https://github.com/bokeh/bokeh/pull/6340
    toggle.callback = CustomJS(args={
        'toggle': toggle,
        **model_dict
    },
                               code=open(
                                   os.path.join(os.path.dirname(__file__),
                                                '../static/js/plotjs',
                                                'togglef.js')).read())

    slider = Slider(start=0.,
                    end=15.,
                    value=0.,
                    step=1.,
                    title='binsize (days)')

    callback = CustomJS(
        args={
            'slider': slider,
            'toggle': toggle,
            **model_dict
        },
        code=open(
            os.path.join(os.path.dirname(__file__), '../static/js/plotjs',
                         'stackf.js')).read().replace('default_zp',
                                                      str(PHOT_ZP)).replace(
                                                          'detect_thresh',
                                                          str(DETECT_THRESH)))

    slider.js_on_change('value', callback)

    layout = row(plot, toggle)
    layout = column(slider, layout)

    p1 = Panel(child=layout, title='Flux')

    # now make the mag light curve
    ymax = 1.1 * data['lim_mag']
    ymin = 0.9 * data['lim_mag']

    if len(data['obs']) > 0:
        ymax[data['obs']] = (data['mag'] + data['magerr']) * 1.1
        ymin[data['obs']] = (data['mag'] - data['magerr']) * 0.9

    plot = figure(plot_width=width,
                  plot_height=height,
                  active_drag='box_zoom',
                  tools='box_zoom,wheel_zoom,pan,reset,save',
                  y_range=(np.nanmax(ymax), np.nanmin(ymin)),
                  toolbar_location='above')

    imhover = HoverTool(tooltips=tooltip_format)
    plot.add_tools(imhover)

    model_dict = {}

    for i, (label, df) in enumerate(split):

        key = f'obs{i}'
        model_dict[key] = plot.scatter(x='mjd',
                                       y='mag',
                                       color='color',
                                       marker='circle',
                                       fill_color='color',
                                       alpha='alpha',
                                       source=ColumnDataSource(df[df['obs']]))

        imhover.renderers.append(model_dict[key])

        unobs_source = df[~df['obs']].copy()
        unobs_source.loc[:, 'alpha'] = 0.8

        key = f'unobs{i}'
        model_dict[key] = plot.scatter(x='mjd',
                                       y='lim_mag',
                                       color='color',
                                       marker='inverted_triangle',
                                       fill_color='white',
                                       line_color='color',
                                       alpha='alpha',
                                       source=ColumnDataSource(unobs_source))

        imhover.renderers.append(model_dict[key])

        key = f'bin{i}'
        model_dict[key] = plot.scatter(
            x='mjd',
            y='mag',
            color='color',
            marker='circle',
            fill_color='color',
            source=ColumnDataSource(data=dict(mjd=[],
                                              flux=[],
                                              fluxerr=[],
                                              filter=[],
                                              color=[],
                                              lim_mag=[],
                                              mag=[],
                                              magerr=[],
                                              instrument=[],
                                              stacked=[])))

        imhover.renderers.append(model_dict[key])

        key = 'obserr' + str(i)
        y_err_x = []
        y_err_y = []

        for d, ro in df[df['obs']].iterrows():
            px = ro['mjd']
            py = ro['mag']
            err = ro['magerr']

            y_err_x.append((px, px))
            y_err_y.append((py - err, py + err))

        model_dict[key] = plot.multi_line(
            xs='xs',
            ys='ys',
            color='color',
            alpha='alpha',
            source=ColumnDataSource(data=dict(xs=y_err_x,
                                              ys=y_err_y,
                                              color=df[df['obs']]['color'],
                                              alpha=[1.] *
                                              len(df[df['obs']]))))

        key = f'binerr{i}'
        model_dict[key] = plot.multi_line(
            xs='xs',
            ys='ys',
            color='color',
            source=ColumnDataSource(data=dict(xs=[], ys=[], color=[])))

        key = f'unobsbin{i}'
        model_dict[key] = plot.scatter(
            x='mjd',
            y='lim_mag',
            color='color',
            marker='inverted_triangle',
            fill_color='white',
            line_color='color',
            alpha=0.8,
            source=ColumnDataSource(data=dict(mjd=[],
                                              flux=[],
                                              fluxerr=[],
                                              filter=[],
                                              color=[],
                                              lim_mag=[],
                                              mag=[],
                                              magerr=[],
                                              instrument=[],
                                              stacked=[])))
        imhover.renderers.append(model_dict[key])

        key = f'all{i}'
        model_dict[key] = ColumnDataSource(df)

        key = f'bold{i}'
        model_dict[key] = ColumnDataSource(df[[
            'mjd', 'flux', 'fluxerr', 'mag', 'magerr', 'filter', 'zp',
            'magsys', 'lim_mag', 'stacked'
        ]])

    plot.xaxis.axis_label = 'MJD'
    plot.yaxis.axis_label = 'AB mag'
    plot.toolbar.logo = None

    toggle = CheckboxWithLegendGroup(labels=list(data.label.unique()),
                                     active=list(
                                         range(len(data.label.unique()))),
                                     colors=list(data.color.unique()))

    # TODO replace `eval` with Namespaces
    # https://github.com/bokeh/bokeh/pull/6340
    toggle.callback = CustomJS(args={
        'toggle': toggle,
        **model_dict
    },
                               code=open(
                                   os.path.join(os.path.dirname(__file__),
                                                '../static/js/plotjs',
                                                'togglem.js')).read())

    slider = Slider(start=0.,
                    end=15.,
                    value=0.,
                    step=1.,
                    title='Binsize (days)')

    button = Button(label="Export Bold Light Curve to CSV")
    button.callback = CustomJS(args={
        'slider': slider,
        'toggle': toggle,
        **model_dict
    },
                               code=open(
                                   os.path.join(os.path.dirname(__file__),
                                                '../static/js/plotjs',
                                                "download.js")).read().replace(
                                                    'objname', obj_id).replace(
                                                        'default_zp',
                                                        str(PHOT_ZP)))

    toplay = row(slider, button)
    callback = CustomJS(
        args={
            'slider': slider,
            'toggle': toggle,
            **model_dict
        },
        code=open(
            os.path.join(os.path.dirname(__file__), '../static/js/plotjs',
                         'stackm.js')).read().replace('default_zp',
                                                      str(PHOT_ZP)).replace(
                                                          'detect_thresh',
                                                          str(DETECT_THRESH)))
    slider.js_on_change('value', callback)

    layout = row(plot, toggle)
    layout = column(toplay, layout)

    p2 = Panel(child=layout, title='Mag')

    tabs = Tabs(tabs=[p2, p1])
    return _plot_to_json(tabs)
示例#4
0
def geoplot(
    gdf_in,
    geometry_column="geometry",
    figure=None,
    figsize=None,
    title="",
    xlabel="Longitude",
    ylabel="Latitude",
    xlim=None,
    ylim=None,
    color="blue",
    colormap=None,
    colormap_uselog=False,
    colormap_range=None,
    category=None,
    dropdown=None,
    slider=None,
    slider_range=None,
    slider_name="",
    show_colorbar=True,
    colorbar_tick_format=None,
    xrange=None,
    yrange=None,
    hovertool=True,
    hovertool_columns=[],
    hovertool_string=None,
    simplify_shapes=None,
    tile_provider="CARTODBPOSITRON_RETINA",
    tile_provider_url=None,
    tile_attribution="",
    tile_alpha=1,
    panning=True,
    zooming=True,
    toolbar_location="right",
    show_figure=True,
    return_figure=True,
    return_html=False,
    legend=True,
    webgl=True,
    **kwargs,
):
    """Doc-String: TODO"""

    # Imports:
    import bokeh.plotting
    from bokeh.plotting import show
    from bokeh.models import (
        HoverTool,
        LogColorMapper,
        LinearColorMapper,
        GeoJSONDataSource,
        WheelZoomTool,
        ColorBar,
        BasicTicker,
        LogTicker,
        Select,
        Slider,
        ColumnDataSource,
    )
    from bokeh.models.callbacks import CustomJS
    from bokeh.models.widgets import Dropdown
    from bokeh.palettes import all_palettes
    from bokeh.layouts import row, column

    # Make a copy of the input geodataframe:
    gdf = gdf_in.copy()

    # Check layertypes:
    if type(gdf) != pd.DataFrame:
        layertypes = []
        if "Point" in str(gdf.geom_type.unique()):
            layertypes.append("Point")
        if "Line" in str(gdf.geom_type.unique()):
            layertypes.append("Line")
        if "Polygon" in str(gdf.geom_type.unique()):
            layertypes.append("Polygon")
        if len(layertypes) > 1:
            raise Exception(
                f"Can only plot GeoDataFrames/Series with single type of geometry (either Point, Line or Polygon). Provided is a GeoDataFrame/Series with types: {layertypes}"
            )
    else:
        layertypes = ["Point"]

    # Get and check provided parameters for geoplot:
    figure_options = {
        "title": title,
        "x_axis_label": xlabel,
        "y_axis_label": ylabel,
        "plot_width": 600,
        "plot_height": 400,
        "toolbar_location": toolbar_location,
        "active_scroll": "wheel_zoom",
        "x_axis_type": "mercator",
        "y_axis_type": "mercator",
    }
    if not figsize is None:
        width, height = figsize
        figure_options["plot_width"] = width
        figure_options["plot_height"] = height
    if webgl:
        figure_options["output_backend"] = "webgl"

    if type(gdf) != pd.DataFrame:
        # Convert GeoDataFrame to Web Mercator Projection:
        gdf.to_crs(epsg=3857, inplace=True)

        # Simplify shapes if wanted:
        if isinstance(simplify_shapes, numbers.Number):
            if layertypes[0] in ["Line", "Polygon"]:
                gdf[geometry_column] = gdf[geometry_column].simplify(simplify_shapes)
        elif not simplify_shapes is None:
            raise ValueError(
                "<simplify_shapes> parameter only accepts numbers or None."
            )

    # Check for category, dropdown or slider (choropleth map column):
    category_options = 0
    if not category is None:
        category_options += 1
        category_columns = [category]
    if not dropdown is None:
        category_options += 1
        category_columns = dropdown
    if not slider is None:
        category_options += 1
        category_columns = slider
    if category_options > 1:
        raise ValueError(
            "Only one of <category>, <dropdown> or <slider> parameters is allowed to be used at once."
        )

    # Check for category (single choropleth plot):
    if category is None:
        pass
    elif isinstance(category, (list, tuple)):
        raise ValueError(
            "For <category>, please provide an existing single column of the GeoDataFrame."
        )
    elif category in gdf.columns:
        pass
    else:
        raise ValueError(
            f"Could not find column '{category}' in GeoDataFrame. For <category>, please provide an existing single column of the GeoDataFrame."
        )

    # Check for dropdown (multiple choropleth plots via dropdown selection):
    if dropdown is None:
        pass
    elif not isinstance(dropdown, (list, tuple)):
        raise ValueError(
            "For <dropdown>, please provide a list/tuple of existing columns of the GeoDataFrame."
        )
    else:
        for col in dropdown:
            if col not in gdf.columns:
                raise ValueError(
                    f"Could not find column '{col}' for <dropdown> in GeoDataFrame. "
                )

    # Check for slider (multiple choropleth plots via slider selection):
    if slider is None:
        pass
    elif not isinstance(slider, (list, tuple)):
        raise ValueError(
            "For <slider>, please provide a list/tuple of existing columns of the GeoDataFrame."
        )
    else:
        for col in slider:
            if col not in gdf.columns:
                raise ValueError(
                    f"Could not find column '{col}' for <slider> in GeoDataFrame. "
                )

        if not slider_range is None:
            if not isinstance(slider_range, Iterable):
                raise ValueError(
                    "<slider_range> has to be a type that is iterable like list, tuple, range, ..."
                )
            else:
                slider_range = list(slider_range)
                if len(slider_range) != len(slider):
                    raise ValueError(
                        "The number of elements in <slider_range> has to be the same as in <slider>."
                    )
                steps = []
                for i in range(len(slider_range) - 1):
                    steps.append(slider_range[i + 1] - slider_range[i])

                if len(set(steps)) > 1:
                    raise ValueError(
                        "<slider_range> has to have equal step size between each elements (like a range-object)."
                    )
                else:
                    slider_step = steps[0]
                    slider_start = slider_range[0]
                    slider_end = slider_range[-1]

    # Check colormap if either <category>, <dropdown> or <slider> is choosen:
    if category_options == 1:
        if colormap is None:
            colormap = blue_colormap
        elif isinstance(colormap, (tuple, list)):
            if len(colormap) > 1:
                pass
            else:
                raise ValueError(
                    f"<colormap> only accepts a list/tuple of at least two colors or the name of one of the following predefined colormaps (see also https://bokeh.pydata.org/en/latest/docs/reference/palettes.html ): {list(all_palettes.keys())}"
                )
        elif isinstance(colormap, str):
            if colormap in all_palettes:
                colormap = all_palettes[colormap]
                colormap = colormap[max(colormap.keys())]
            else:
                raise ValueError(
                    f"Could not find <colormap> with name {colormap}. The following predefined colormaps are supported (see also https://bokeh.pydata.org/en/latest/docs/reference/palettes.html ): {list(all_palettes.keys())}"
                )
        else:
            raise ValueError(
                f"<colormap> only accepts a list/tuple of at least two colors or the name of one of the following predefined colormaps (see also https://bokeh.pydata.org/en/latest/docs/reference/palettes.html ): {list(all_palettes.keys())}"
            )
    else:
        if isinstance(color, str):
            colormap = [color]
        elif color is None:
            colormap = ["blue"]
        else:
            raise ValueError(
                "<color> has to be a string specifying the fill_color of the map glyph."
            )

    # Check xlim & ylim:
    if xlim is not None:
        if isinstance(xlim, (tuple, list)):
            if len(xlim) == 2:
                xmin, xmax = xlim
                for _ in [xmin, xmax]:
                    if not -180 < _ <= 180:
                        raise ValueError(
                            "Limits for x-axis (=Longitude) have to be between -180 and 180."
                        )
                if not xmin < xmax:
                    raise ValueError("xmin has to be smaller than xmax.")

                from pyproj import Transformer
                transformer = Transformer.from_crs("epsg:4326", "epsg:3857")
                xmin = transformer.transform(0, xmin)[0]
                xmax = transformer.transform(0, xmax)[0]
                figure_options["x_range"] = (xmin, xmax)
            else:
                raise ValueError(
                    "Limits for x-axis (=Longitude) have to be of form [xmin, xmax] with values between -180 and 180."
                )
        else:
            raise ValueError(
                "Limits for x-axis (=Longitude) have to be of form [xmin, xmax] with values between -180 and 180."
            )
    if ylim is not None:
        if isinstance(ylim, (tuple, list)):
            if len(ylim) == 2:
                ymin, ymax = ylim
                for _ in [ymin, ymax]:
                    if not -90 < _ <= 90:
                        raise ValueError(
                            "Limits for y-axis (=Latitude) have to be between -90 and 90."
                        )
                if not ymin < ymax:
                    raise ValueError("ymin has to be smaller than ymax.")

                from pyproj import Transformer
                transformer = Transformer.from_crs("epsg:4326", "epsg:3857")
                ymin = transformer.transform(ymin,0)[1]
                ymax = transformer.transform(ymax,0)[1]
                figure_options["y_range"] = (ymin, ymax)
            else:
                raise ValueError(
                    "Limits for y-axis (=Latitude) have to be of form [ymin, ymax] with values between -90 and 90."
                )
        else:
            raise ValueError(
                "Limits for y-axis (=Latitude) have to be of form [ymin, ymax] with values between -90 and 90."
            )

    # Create Figure to draw:
    old_layout = None
    if figure is None:
        p = bokeh.plotting.figure(**figure_options)

        # Add Tile Source as Background:
        p = _add_backgroundtile(
            p, tile_provider, tile_provider_url, tile_attribution, tile_alpha
        )

    elif isinstance(figure, type(bokeh.plotting.figure())):
        p = figure
    elif isinstance(figure, type(column())):
        old_layout = figure
        p = _get_figure(old_layout)
    else:
        raise ValueError(
            "Parameter <figure> has to be of type bokeh.plotting.figure or bokeh.layouts.column."
        )

    # Get ridd of zoom on axes:
    for t in p.tools:
        if type(t) == WheelZoomTool:
            t.zoom_on_axis = False

    # Hide legend if wanted:
    legend_input = legend
    if isinstance(legend, str):
        pass
    else:
        legend = "GeoLayer"

    # Define colormapper:
    if len(colormap) == 1:
        kwargs["fill_color"] = colormap[0]

    elif not category is None:
        # Check if category column is numerical:
        if not issubclass(gdf[category].dtype.type, np.number):
            raise NotImplementedError(
                f"<category> plot only yet implemented for numerical columns. Column '{category}' is not numerical."
            )

        field = category
        colormapper_options = {"palette": colormap}
        if not colormap_range is None:
            if not isinstance(colormap_range, (tuple, list)):
                raise ValueError(
                    "<colormap_range> can only be 'None' or a tuple/list of form (min, max)."
                )
            elif len(colormap_range) == 2:
                colormapper_options["low"] = colormap_range[0]
                colormapper_options["high"] = colormap_range[1]
        else:
            colormapper_options["low"] = gdf[field].min()
            colormapper_options["high"] = gdf[field].max()
        if colormap_uselog:
            colormapper = LogColorMapper(**colormapper_options)
        else:
            colormapper = LinearColorMapper(**colormapper_options)
        kwargs["fill_color"] = {"field": "Colormap", "transform": colormapper}
        if not isinstance(legend, str):
            legend = str(field)

    elif not dropdown is None:
        # Check if all columns in dropdown selection are numerical:
        for col in dropdown:
            if not issubclass(gdf[col].dtype.type, np.number):
                raise NotImplementedError(
                    f"<dropdown> plot only yet implemented for numerical columns. Column '{col}' is not numerical."
                )

        field = dropdown[0]
        colormapper_options = {"palette": colormap}
        if not colormap_range is None:
            if not isinstance(colormap_range, (tuple, list)):
                raise ValueError(
                    "<colormap_range> can only be 'None' or a tuple/list of form (min, max)."
                )
            elif len(colormap_range) == 2:
                colormapper_options["low"] = colormap_range[0]
                colormapper_options["high"] = colormap_range[1]
        else:
            colormapper_options["low"] = gdf[dropdown].min().min()
            colormapper_options["high"] = gdf[dropdown].max().max()
        if colormap_uselog:
            colormapper = LogColorMapper(**colormapper_options)
        else:
            colormapper = LinearColorMapper(**colormapper_options)
        kwargs["fill_color"] = {"field": "Colormap", "transform": colormapper}
        legend = " " + field

    elif not slider is None:
        # Check if all columns in dropdown selection are numerical:
        for col in slider:
            if not issubclass(gdf[col].dtype.type, np.number):
                raise NotImplementedError(
                    f"<slider> plot only yet implemented for numerical columns. Column '{col}' is not numerical."
                )

        field = slider[0]
        colormapper_options = {"palette": colormap}
        if not colormap_range is None:
            if not isinstance(colormap_range, (tuple, list)):
                raise ValueError(
                    "<colormap_range> can only be 'None' or a tuple/list of form (min, max)."
                )
            elif len(colormap_range) == 2:
                colormapper_options["low"] = colormap_range[0]
                colormapper_options["high"] = colormap_range[1]
        else:
            colormapper_options["low"] = gdf[slider].min().min()
            colormapper_options["high"] = gdf[slider].max().max()
        if colormap_uselog:
            colormapper = LogColorMapper(**colormapper_options)
        else:
            colormapper = LinearColorMapper(**colormapper_options)
        kwargs["fill_color"] = {"field": "Colormap", "transform": colormapper}
        if not isinstance(legend, str):
            legend = "Geolayer"

    # Check that only hovertool_columns or hovertool_string is used:
    if isinstance(hovertool_columns, (list, tuple, str)):
        if len(hovertool_columns) > 0 and hovertool_string is not None:
            raise ValueError(
                "Either <hovertool_columns> or <hovertool_string> can be used, but not both at the same time."
            )
    else:
        raise ValueError(
            "<hovertool_columns> has to be a list of columns of the GeoDataFrame or the string 'all'."
        )

    if hovertool_string is not None:
        hovertool_columns = "all"

    # Check for Hovertool columns:
    if hovertool:
        if not isinstance(hovertool_columns, (list, tuple)):
            if hovertool_columns == "all":
                hovertool_columns = list(
                    filter(lambda col: col != geometry_column, gdf.columns)
                )
            else:
                raise ValueError(
                    "<hovertool_columns> has to be a list of columns of the GeoDataFrame or the string 'all'."
                )
        elif len(hovertool_columns) == 0:
            if not category is None:
                hovertool_columns = [category]
            elif not dropdown is None:
                hovertool_columns = dropdown
            elif not slider is None:
                hovertool_columns = slider
            else:
                hovertool_columns = []
        else:
            for col in hovertool_columns:
                if col not in gdf.columns:
                    raise ValueError(
                        f"Could not find columns '{col}' in GeoDataFrame. <hovertool_columns> has to be a list of columns of the GeoDataFrame or the string 'all'."
                    )
    else:
        if category is None:
            hovertool_columns = []
        else:
            hovertool_columns = [category]

    # Reduce DataFrame to needed columns:
    if type(gdf) == pd.DataFrame:
        gdf["Geometry"] = 0
        additional_columns = ["x", "y"]
    else:
        additional_columns = [geometry_column]
    for kwarg, value in kwargs.items():
        if isinstance(value, Hashable):
            if value in gdf.columns:
                additional_columns.append(value)
    if category_options == 0:
        gdf = gdf[list(set(hovertool_columns) | set(additional_columns))]
    else:
        gdf = gdf[
            list(
                set(hovertool_columns) | set(category_columns) | set(additional_columns)
            )
        ]
        gdf["Colormap"] = gdf[field]
        field = "Colormap"

    # Create GeoJSON DataSource for Plot:
    if type(gdf) != pd.DataFrame:
        geo_source = GeoJSONDataSource(geojson=gdf.to_json())
    else:
        geo_source = gdf

    # Draw Glyph on Figure:
    layout = None
    if "Point" in layertypes:
        if "line_color" not in kwargs:
            kwargs["line_color"] = kwargs["fill_color"]
        glyph = p.scatter(
            x="x", y="y", source=geo_source, legend_label=legend, **kwargs
        )

    if "Line" in layertypes:
        if "line_color" not in kwargs:
            kwargs["line_color"] = kwargs["fill_color"]
            del kwargs["fill_color"]
        glyph = p.multi_line(
            xs="xs", ys="ys", source=geo_source, legend_label=legend, **kwargs
        )

    if "Polygon" in layertypes:

        if "line_color" not in kwargs:
            kwargs["line_color"] = "black"

        # Creates from a geoDataFrame with Polygons and Multipolygons a Pandas DataFrame
        # with x any y columns specifying the geometry of the Polygons:
        geo_source = ColumnDataSource(
            convert_geoDataFrame_to_patches(gdf, geometry_column)
        )

        # Plot polygons:
        glyph = p.multi_polygons(
            xs="__x__", ys="__y__", source=geo_source, legend_label=legend, **kwargs
        )

    # Add hovertool:
    if hovertool and (category_options == 1 or len(hovertool_columns) > 0):
        my_hover = HoverTool(renderers=[glyph])
        if hovertool_string is None:
            my_hover.tooltips = [(str(col), "@{%s}" % col) for col in hovertool_columns]
        else:
            my_hover.tooltips = hovertool_string
        p.add_tools(my_hover)

    # Add colorbar:
    if show_colorbar and category_options == 1:
        colorbar_options = {
            "color_mapper": colormapper,
            "label_standoff": 12,
            "border_line_color": None,
            "location": (0, 0),
        }
        if colormap_uselog:
            colorbar_options["ticker"] = LogTicker()

        if colorbar_tick_format:
            colorbar_options["formatter"] = get_tick_formatter(colorbar_tick_format)

        colorbar = ColorBar(**colorbar_options)

        p.add_layout(colorbar, "right")

    # Add Dropdown Widget:
    if not dropdown is None:
        # Define Dropdown widget:
        dropdown_widget = Select(
            title="Select Choropleth Layer", options=list(zip(dropdown, dropdown))
        )

        # Define Callback for Dropdown widget:
        callback = CustomJS(
            args=dict(
                dropdown_widget=dropdown_widget,
                geo_source=geo_source,
                legend=p.legend[0].items[0],
            ),
            code="""

                //Change selection of field for Colormapper for choropleth plot:
                geo_source.data["Colormap"] = geo_source.data[dropdown_widget.value];
                geo_source.change.emit();

                //Change label of Legend:
                legend.label["value"] = " " + dropdown_widget.value;

                            """,
        )
        dropdown_widget.js_on_change("value", callback)

        # Add Dropdown widget above the plot:
        if old_layout is None:
            layout = column(dropdown_widget, p)
        else:
            layout = column(dropdown_widget, old_layout)

    # Add Slider Widget:
    if not slider is None:

        if slider_range is None:
            slider_start = 0
            slider_end = len(slider) - 1
            slider_step = 1

        value2name = ColumnDataSource(
            {
                "Values": np.arange(
                    slider_start, slider_end + slider_step, slider_step
                ),
                "Names": slider,
            }
        )

        # Define Slider widget:
        slider_widget = Slider(
            start=slider_start,
            end=slider_end,
            value=slider_start,
            step=slider_step,
            title=slider_name,
        )

        # Define Callback for Slider widget:
        callback = CustomJS(
            args=dict(
                slider_widget=slider_widget,
                geo_source=geo_source,
                value2name=value2name,
            ),
            code="""

                //Change selection of field for Colormapper for choropleth plot:
                var slider_value = slider_widget.value;
                var i;
                for(i=0; i<value2name.data["Names"].length; i++)
                    {
                    if (value2name.data["Values"][i] == slider_value)
                        {
                         var name = value2name.data["Names"][i];
                         }

                    }
                geo_source.data["Colormap"] = geo_source.data[name];
                geo_source.change.emit();

                            """,
        )
        slider_widget.js_on_change("value", callback)

        # Add Slider widget above the plot:
        if old_layout is None:
            layout = column(slider_widget, p)
        else:
            layout = column(slider_widget, old_layout)

    # Hide legend if user wants:
    if legend_input is False:
        p.legend.visible = False

    # Set click policy for legend:
    p.legend.click_policy = "hide"

    # Set panning option:
    if panning is False:
        p.toolbar.active_drag = None

    # Set zooming option:
    if zooming is False:
        p.toolbar.active_scroll = None

    # Display plot and if wanted return plot:
    if layout is None:
        if old_layout is None:
            layout = p
        else:
            layout = old_layout

    # Display plot if wanted
    if show_figure:
        show(layout)

    # Return as (embeddable) HTML if wanted:
    if return_html:
        return embedded_html(layout)

    # Return plot:
    if return_figure:
        return layout
                              offset=offset),
                    code="""
    const data = source.data;
    const A = amp.value;
    const k = freq.value;
    const phi = phase.value;
    const B = offset.value;
    const x = data['x']
    const y = data['y']
    for (let i = 0; i < x.length; i++) {
        y[i] = B + A*Math.sin(k*x[i]+phi);
    }
    source.change.emit();
""")

amp.js_on_change('value', callback)
freq.js_on_change('value', callback)
phase.js_on_change('value', callback)
offset.js_on_change('value', callback)

heading = Div(
    sizing_mode="stretch_width",
    height=80,
    text="In this wave example, the sliders on the left "
    "can be used to change the amplitude, frequency, phase, and offset of the wave."
)

layout = column(heading, row(widgets, plot), sizing_mode="stretch_both")

output_file("slider.html", title="slider.py example")
示例#6
0
def addGenomeTools(fig, geneRecs, genomeRecs, geneSource, genomeSource, nb,
                   geneLabels):
    # add genome labels
    genomeLabels = LabelSet(x='x_label',
                            y='y',
                            x_offset=-20,
                            text='name',
                            text_align="right",
                            source=genomeSource,
                            render_mode='canvas',
                            text_font_size="16px")
    fig.add_layout(genomeLabels)

    slider_font = Slider(start=0,
                         end=64,
                         value=16,
                         step=1,
                         title="Genome label font size in px")
    slider_font.js_on_change(
        'value',
        CustomJS(args=dict(other=genomeLabels),
                 code="other.text_font_size = this.value+'px';"))

    slider_offset = Slider(start=-400,
                           end=0,
                           value=-20,
                           step=1,
                           title="Genome label offset")
    slider_offset.js_link('value', genomeLabels, 'x_offset')

    slider_spacing = Slider(start=1,
                            end=40,
                            value=10,
                            step=1,
                            title="Genomes spacing")
    slider_spacing.js_on_change(
        'value',
        CustomJS(args=dict(geneRecs=geneRecs,
                           geneSource=geneSource,
                           genomeRecs=genomeRecs,
                           genomeSource=genomeSource,
                           nb_elements=nb,
                           genomeLabels=genomeLabels,
                           geneLabels=geneLabels),
                 code="""
            var current_val = genomeSource.data['y'][genomeSource.data['y'].length - 1] / (nb_elements-1);
            for (let i=0 ; i < genomeSource.data['y'].length ; i++){
                genomeSource.data['y'][i] =  (genomeSource.data['y'][i] * this.value) / current_val;
            }
            for (let i=0 ; i < geneSource.data['y'].length ; i++){
                if((geneSource.data['ordered'][i] == 'True' && geneSource.data['strand'][i] == '+') || (geneSource.data['ordered'][i] == 'False' && geneSource.data['strand'][i] == '-') ){
                    geneSource.data['y'][i] = (((geneSource.data['y'][i]-1) * this.value) / current_val) +1;
                    geneSource.data['y_label'][i] = (((geneSource.data['y_label'][i]-1-1.5) * this.value) / current_val) + 1 + 1.5;
                }else{
                    geneSource.data['y'][i] = (((geneSource.data['y'][i]+1) * this.value) / current_val) -1;
                    geneSource.data['y_label'][i] = (((geneSource.data['y_label'][i]+1-1.5) * this.value) / current_val) -1 + 1.5;

                }
            }
            geneRecs.source = geneSource;
            genomeRecs.source = genomeSource;
            geneLabels.source = geneSource;
            genomeLabels.source = genomeSource;
            geneSource.change.emit();
            genomeSource.change.emit();
        """))

    genome_header = Div(text="<b>Genomes:</b>")
    return column(genome_header, slider_spacing, slider_font, slider_offset)
示例#7
0
    for (var i = 0; i < lblx.length; i++) {
        lbly[i] = lb(lblx[i], VMAX, KM);
    }
    for (var i = 0; i < lbpx.length; i++) {
        lbpy[i] = lb(lbpx[i], VMAX, KM);
    }
    
    // change data sources
    mmLineSource.change.emit();
    mmPointSource.change.emit();
    lbLineSource.change.emit();
    lbPointSource.change.emit();
""")

# add sliders to plot and display
vmax_slider.js_on_change('value', callback)
km_slider.js_on_change('value', callback)

# -------------------------------------------------
# create baseline michaelis-menten plot
# -------------------------------------------------
layout = row(
    mm_plot,
    lb_plot,
    column(vmax_slider, km_slider),
)

output_file(html_output_dir + "04-dual-lb-mm-plot.html",
            title="Michaelis-Menten Kinetics")
show(layout)
示例#8
0
def plot_surfaces_side_by_side(fig_kwargs=None):
    sample_size = 10
    _x = x[:sample_size]
    _y = y[:sample_size]
    m_hat = ((_y.sum() * _x.sum() / sample_size - (_y * _x).sum()) /
             ((_x.sum())**2 / sample_size - (_x**2).sum()))
    k_hat = _y.sum() / sample_size - m_hat * _x.sum() / sample_size
    fit_source = ColumnDataSource({"slope": [m_hat], "intercept": [k_hat]})

    figures = []
    fig_kwargs = fig_kwargs or {}
    for sample_size in [None, sample_size]:
        width = fig_kwargs.get("width", 400)
        if sample_size is not None:
            width += 200
        fig_kwargs["width"] = width

        p = _init_surface_plot(sample_size, fig_kwargs)
        source, mapper = _compute_cost_surface(sample_size)
        if sample_size is None:
            global_mapper = mapper
        p.image("log",
                x="x",
                y="y",
                dw="dw",
                dh="dh",
                color_mapper=global_mapper,
                source=source,
                name="img",
                level="image")
        r = p.plus([b_star], [m_star],
                   line_color="#000000",
                   line_width=0.8,
                   fill_color="#ffffff",
                   size=10)
        items = [LegendItem(renderers=[r], label="True best parameters")]

        r = p.plus("intercept",
                   "slope",
                   line_color="#000000",
                   line_width=1.5,
                   fill_color="#000000",
                   size=10,
                   source=fit_source)
        items.append(LegendItem(renderers=[r], label="Best fit on sample"))

        p.add_tools(
            HoverTool(renderers=p.select("img"),
                      tooltips=[("Slope", "$y"), ("Intercept", "$x"),
                                ("Expected Error", "@img")]))
        figures.append(p)

    p_true, p_emp = figures
    p_emp.add_layout(Legend(items=items), "right")

    js_code = """
        var img_data = img_src.data; // data from the source containing the image
        var sample_data = sample_src.data; // data from the source containing our samples
        var n = cb_obj.value; // current value of the slider
        var img_dim = {}; // format the string with the grid dimension

        // initialize all the variables we'll need
        var pixel;
        var slope;
        var intercept;
        var error;
        var idx;

        // loop through each slope/intercept combination, calculate
        // the average error over the current number of samples, then
        // update that pixel in the image source's data
        for (var i = 0; i < img_dim; i++) {{
            for (var j = 0; j < img_dim; j++) {{
                pixel = 0;
                idx = i*img_dim + j;
                for (var k = 0; k < n; k++) {{
                    slope = img_data["slope"][0][idx]
                    intercept = img_data["intercept"][0][idx]
                    error = slope*sample_data["x"][k] + intercept - sample_data["y"][k];
                    pixel += Math.pow(error, 2);
                }}
                pixel /= n;
                img_data["log"][0][idx] = Math.log(pixel);
                img_data["img"][0][idx] = pixel;
            }}
        }}

        // have the source update all its renderers to reflect the new data
        img_src.change.emit();

        // now do our regression fit in JS land to
        // move the best fit cross around the screen
        var fit_data = fit_src.data;

        // push all n samples to our subsample source
        // compute sums we'll need to do regression fit
        var x;
        var y;
        var xsum = 0;
        var ysum = 0;
        var x2sum = 0;
        var xysum = 0;
        for (var i = 0; i < n; i ++) {{
            x = sample_data["x"][i];
            y = sample_data["y"][i];

            xsum += x;
            ysum += y;
            x2sum += Math.pow(x, 2);
            xysum += x*y;
        }}

        // update best fit values for regression
        var denom = n*x2sum - Math.pow(xsum, 2);
        var m = (n*xysum - xsum*ysum) / denom;
        var b = (ysum*x2sum - xsum*xysum) / denom;
        fit_data["intercept"] = [b];
        fit_data["slope"] = [m];

        // update all source renderers
        fit_src.change.emit();
    """.format(source.data["img"][0].shape[0])

    slider = Slider(start=1,
                    end=N,
                    step=1,
                    value=sample_size,
                    title="Observations Used for Fit",
                    orientation="horizontal",
                    direction="ltr")
    callback = CustomJS(args={
        "img_src": source,
        "sample_src": ColumnDataSource(data),
        "fit_src": fit_source
    },
                        code=js_code)
    slider.js_on_change("value", callback)

    return column(row(*figures), slider)
        return (bottom + ((top - bottom) / (1 + Math.pow(10, ((ec50 + x) * hillslope * -1)))));
    }

    // loop over data and edit
    for (var i = 0; i < lx.length; i++) {
        ly[i] = fpl(lx[i], TOP, BOTTOM, EC50, HILL);
    }

    for (var i = 0; i < px.length; i++) {
        py[i] = fpl(px[i], TOP, BOTTOM, EC50, HILL);
    }

    // emit changes
    LineSource.change.emit();
    PointSource.change.emit();
""")

# add sliders to plot and display
top_slider.js_on_change('value', callback)
bottom_slider.js_on_change('value', callback)
ec50_slider.js_on_change('value', callback)
hill_slider.js_on_change('value', callback)

layout = row(
    plot,
    column(top_slider, bottom_slider, ec50_slider, hill_slider),
)

output_file(html_output_dir + "10-receptors-dr.html", title="Dose Response")
show(layout)
示例#10
0
    const lx = LineData['x']
    const ly = LineData['y']
    const px = PointData['x']
    const py = PointData['y']
    for (var i = 0; i < lx.length; i++) {
        ly[i] = (VMAX*lx[i])/((KM*(1+(CI/KI))+lx[i]));
    }
    LineSource.change.emit();
    for (var i = 0; i < px.length; i++) {
    py[i] = (VMAX*px[i])/((KM*(1+(CI/KI))+px[i]));
    }
    PointSource.change.emit();
""")

# add sliders to plot and display
ci_slider.js_on_change('value', callback)
ki_slider.js_on_change('value', callback)

layout = row(
    plot,
    column(ci_slider, ki_slider),
)
show(layout)

# ----------------------------------------------------------------------------------------------------------------------
# noncompetitive inhibition
# ----------------------------------------------------------------------------------------------------------------------
# generate data for plotting
log_start = -1
log_end = 4
示例#11
0
                              freq=freq_slider,
                              phase=phase_slider,
                              offset=offset_slider),
                    code="""
    const data = source.data;
    const A = amp.value;
    const k = freq.value;
    const phi = phase.value;
    const B = offset.value;
    const x = data['x']
    const y = data['y']
    for (let i = 0; i < x.length; i++) {
        y[i] = B + A*Math.sin(k*x[i]+phi);
    }
    source.change.emit();
""")

amp_slider.js_on_change('value', callback)
freq_slider.js_on_change('value', callback)
phase_slider.js_on_change('value', callback)
offset_slider.js_on_change('value', callback)

layout = row(
    plot,
    column(amp_slider, freq_slider, phase_slider, offset_slider),
)

output_file("slider.html", title="slider.py example")

show(layout)
示例#12
0
class MyGraph:
    def __init__(self):
        self._graph_layout = side_pos
        self.slider = Slider(start=1, end=60, value=7, step=1, title="X Zoom")
        self.plot = bokeh.plotting.figure(plot_width=400,
                                          plot_height=400,
                                          x_range=Range1d(-1,
                                                          -1 +
                                                          self.slider.value,
                                                          bounds=(-2, 4e6)),
                                          y_range=Range1d(0,
                                                          1,
                                                          bounds=(-0.5, 1.5)),
                                          tools='')

        self.slider.js_on_change(
            'value',
            CustomJS(args=dict(slider=self.slider, plot=self.plot),
                     code='''
                   var days = slider.value;
                   plot.x_range.end=plot.x_range.start + days;
                   plot.x_range.change.emit();
                '''))
        self.xSelectSwitch = RadioButtonGroup(
            labels=['By Generation', 'By Date'], active=0)
        self.plot.toolbar.logo = None
        self.plot.title.text = "Samples"
        self.plot.yaxis.visible = False
        self.plot.ygrid.visible = False
        self.plot.xaxis[0].ticker.min_interval = 1
        self.plot.xaxis[0].ticker.num_minor_ticks = 0
        self.plot.xaxis[0].axis_label = 'Generations'
        self.pallete = bokeh.palettes.Spectral4
        tools = [TapTool(), WheelZoomTool(), PanTool(), ResetTool()]
        # HoverTool(tooltips={'ID':"@id",'Type':'@type','Notes':'@notes'})]
        tools[1].dimensions = 'height'  # vertical zoom only
        self.plot.add_tools(*tools)
        self.plot.toolbar.active_scroll = tools[
            1]  # sets the scroll zoom to be active immediately.
        self.colors = ['red', 'blue', 'green', 'purple', 'cyan', 'yellow']
        self.widget = Column(self.plot, self.slider, self.xSelectSwitch)

    def getFromDB(self, sqlsession):
        self._graph = nx.DiGraph()
        self.nodelist = [(i.id, i.toJSON())
                         for i in sqlsession.query(Sample).all()]
        '''Generate Networkx graph'''
        self._graph.add_nodes_from(self.nodelist)
        edges = [(i[0], j) for i in self.nodelist for j in i[1]['children']]
        self._graph.add_edges_from(edges)
        '''Load nx graph to bokeh renderer'''
        self.renderer: bokeh.models.GraphRenderer = bokeh.plotting.from_networkx(
            self._graph, self._graph_layout)

        # use the first item in the nodelist to generate the possible data sources for the hover tool
        for k, v in self.nodelist[0][1].items():
            self.renderer.node_renderer.data_source.add(
                [i[1][k] for i in self.nodelist], name=k)
        import pdb
        pdb.set_trace()
        self.renderer.node_renderer.data_source.add(  # Set color based on sampletype
            [
                self.colors[Sample.Type[nodeData['type']].value - 1]
                for nodeId, nodeData in self.nodelist
            ], 'color')
        self.renderer.node_renderer.glyph = bokeh.models.Circle(
            size=20, fill_color='color')  # self.pallete[0])
        self.renderer.node_renderer.selection_glyph = bokeh.models.Circle(
            size=15, fill_color='color')
        self.renderer.node_renderer.hover_glyph = bokeh.models.Circle(
            size=15, fill_color=self.pallete[1])

        self.renderer.edge_renderer.glyph = bokeh.models.MultiLine(
            line_color="#CCCCCC", line_alpha=0.8, line_width=5)
        self.renderer.edge_renderer.selection_glyph = bokeh.models.MultiLine(
            line_color=self.pallete[0], line_width=5)
        self.renderer.edge_renderer.hover_glyph = bokeh.models.MultiLine(
            line_color=self.pallete[1], line_width=5)

        self.renderer.selection_policy = bokeh.models.graphs.NodesAndLinkedEdges(
        )
        self.renderer.inspection_policy = bokeh.models.graphs.NodesOnly()

        try:
            oldrenderer = \
            [(i, v) for i, v in enumerate(self.plot.renderers) if isinstance(v, bokeh.models.GraphRenderer)][0]
            oldsel = oldrenderer[1].node_renderer.data_source.selected
        except:
            oldsel = None
            oldrenderer = None
        if oldrenderer:
            self.plot.renderers.pop(oldrenderer[0])
        self.plot.renderers.append(self.renderer)
        # If something was previously selected add it reselect it now
        if oldsel:
            self.renderer.node_renderer.data_source.selected.indices = oldsel.indices
示例#13
0
    def _get_widgets(self,
                     all_glyphs,
                     overtime_groups,
                     run_groups,
                     slider_labels=None):
        """Combine timeslider for quantiles and checkboxes for individual runs in a single javascript-snippet

        Parameters
        ----------
        all_glyphs: List[Glyph]
            togglable bokeh-glyphs
        overtime_groups, run_groups: Dicŧ[str -> List[int]
            mapping labels to indices of the all_glyphs-list
        slider_labels: Union[None, List[str]]
            if provided, used as labels for timeslider-widget

        Returns
        -------
        time_slider, checkbox, select_all, select_none: Widget
            desired interlayed bokeh-widgets
        checkbox_title: Div
            text-element to "show title" of checkbox
        """
        aliases = ['glyph' + str(idx) for idx, _ in enumerate(all_glyphs)]
        labels_overtime = list(overtime_groups.keys())
        labels_runs = list(run_groups.keys())

        code = ""
        # Define javascript variable with important arrays
        code += "var glyphs = [" + ", ".join(aliases) + "];"
        code += "var overtime = [" + ','.join([
            '[' + ','.join(overtime_groups[l]) + ']' for l in labels_overtime
        ]) + '];'
        code += "var runs = [" + ','.join(
            ['[' + ','.join(run_groups[l]) + ']' for l in labels_runs]) + '];'
        # Deactivate all glyphs
        code += """
        glyphs.forEach(function(g) {
          g.visible = false;
        })"""
        # Add function for array-union (to combine all relevant glyphs for the different runs)
        code += """
        // union function
        function union_arrays(x, y) {
          var obj = {};
          for (var i = x.length-1; i >= 0; -- i)
             obj[x[i]] = x[i];
          for (var i = y.length-1; i >= 0; -- i)
             obj[y[i]] = y[i];
          var res = []
          for (var k in obj) {
            if (obj.hasOwnProperty(k))  // <-- optional
              res.push(obj[k]);
          }
          return res;
        }"""
        # Add logging
        code += """
        console.log("Timeslider: " + time_slider.value);
        console.log("Checkbox: " + checkbox.active);"""
        # Set timeslider title (to enable log-scale and print wallclocktime-labels)
        if slider_labels:
            code += "var slider_labels = " + str(slider_labels) + ";"
            code += "console.log(\"Detected slider_labels: \" + slider_labels);"
            code += "time_slider.title = \"Until wallclocktime \" + slider_labels[time_slider.value - 1] + \". Step no.\"; "
            title = "Until wallclocktime " + slider_labels[-1] + ". Step no. "
        else:
            title = "Quantile on {} scale".format(
                "logarithmic" if self.timeslider_log else "linear")
            code += "time_slider.title = \"{}\";".format(title)
        # Combine checkbox-arrays, intersect with time_slider and set all selected glyphs to true
        code += """
        var activate = [];
        // if we want multiple checkboxes at the same time, we need to combine the arrays
        checkbox.active.forEach(function(c) {
          activate = union_arrays(activate, runs[c]);
        })
        // now the intersection of timeslider-activated and checkbox-activated
        activate = activate.filter(value => -1 !== overtime[time_slider.value - 1].indexOf(value));
        activate.forEach(function(idx) {
          glyphs[idx].visible = true;
        })
        """

        num_quantiles = len(overtime_groups)
        if num_quantiles > 1:
            timeslider = Slider(start=1,
                                end=num_quantiles,
                                value=num_quantiles,
                                step=1,
                                title=title)
        else:
            timeslider = Slider(start=1, end=2, value=1)
        labels_runs = [
            label.replace('_', ' ') if label.startswith('budget') else label
            for label in labels_runs
        ]
        checkbox = CheckboxButtonGroup(labels=labels_runs,
                                       active=list(range(len(labels_runs))))

        args = {name: glyph for name, glyph in zip(aliases, all_glyphs)}
        args['time_slider'] = timeslider
        args['checkbox'] = checkbox
        callback = CustomJS(args=args, code=code)
        timeslider.js_on_change('value', callback)
        checkbox.callback = callback
        checkbox_title = Div(text="Showing only configurations evaluated in:")

        # Add all/none button to checkbox
        code_all = "checkbox.active = " + str(list(range(
            len(labels_runs)))) + ";" + code
        code_none = "checkbox.active = [];" + code
        select_all = Button(label="All",
                            callback=CustomJS(args=args, code=code_all))
        select_none = Button(label="None",
                             callback=CustomJS(args=args, code=code_none))

        return timeslider, checkbox, select_all, select_none, checkbox_title
示例#14
0
def roi_plot(roi, *args, **kwargs):
    from tramway.helper import map_plot
    from .map import scalar_map_2d, plot_points

    feature_name = kwargs.pop("feature")

    kwargs.update(dict(use_bokeh=True, show=False))
    if "point_style" not in kwargs:
        kwargs["point_style"] = dict(color="r", alpha=0.1)
    fig = map_plot(*args, **kwargs)
    full_fov_fig = fig[0]

    patches_x, patches_y = [], []
    for center, bounding_box in roi:
        patch_x = bounding_box[[0, 0, 2, 2]]
        patch_y = bounding_box[[1, 3, 3, 1]]
        patches_x.append(patch_x)
        patches_y.append(patch_y)

    roi_controller = RoiController(patches_x, patches_y, fill_color=None)
    full_fov_fig.patches(**roi_controller.patches_kwargs)

    roi_controller.unset_active("all")
    first_roi = 0
    roi_controller.set_active(first_roi)

    # right panel

    first_roi_bb = roi[first_roi][1]
    xlim = first_roi_bb[[0, 2]].tolist()
    ylim = first_roi_bb[[1, 3]].tolist()

    from bokeh.plotting import figure, show

    analysis_tree = args[0]
    partition_label, map_label = kwargs["label"]
    cells = analysis_tree[partition_label].data
    _map = analysis_tree[partition_label][map_label].data[feature_name]

    _kwargs = {}
    for _attr in ("clim", ):
        if _attr in kwargs:
            _kwargs[attr] = kwargs[attr]

    focused_fig = figure()
    scalar_map_2d(cells,
                  _map,
                  figure=focused_fig,
                  xlim=xlim,
                  ylim=xlim,
                  **_kwargs)
    plot_points(cells, color="r", alpha=0.1)

    from bokeh.models import Slider, CustomJS

    slider = Slider(start=1, end=len(roi), step=1, value=1, title="roi")
    slider.js_on_change("value", roi_controller.js_callback(focused_fig))

    # plot with layout

    from bokeh.layouts import row, column

    show(
        row(full_fov_fig, column(focused_fig,
                                 slider,
                                 sizing_mode="scale_width")))
示例#15
0
                        scR[0] = R[dia[0]];
                        
                        var j = 0
                        for (var i = ( len * (dia[0] - start)); i < ( len * (dia[0] - start) + len); i++) {
                            Sp[j] = lSp[i]
                            Ip[j] = lIp[i]
                            Rp[j] = lRp[i]
                            j++
                        }
                        
                        s1.change.emit();
                        s2.change.emit();
                    """)


    dias_slider.js_on_change('value', callback)
    plot.legend.location = "top_left"

    if BOKEH_THEME == 'Dark':
        save_name =  './slider' + COUNTRY + 'dark.html'
        my_theme = define_custom_theme('#2C2F38','#2C2F38')
        plot.legend.background_fill_alpha = 0.0
        plot.legend.border_line_alpha = 0
        plot.legend.label_text_color = 'white'
        plot.xaxis.major_tick_line_color = None  # turn off x-axis major ticks
        plot.xaxis.minor_tick_line_color = None  # turn off x-axis minor ticks
        plot.yaxis.major_tick_line_color = None  # turn off y-axis major ticks
        plot.yaxis.minor_tick_line_color = None  # turn off y-axis minor ticks

    elif BOKEH_THEME == 'Light':
        save_name =  './slider' + COUNTRY + '.html'
            y[i] = Math.pow(x[i], f)
        }
        source.change.emit();
    """)


callback_ion = CustomJS(args=dict(source=source), code="""
        var data = source.data;
        var f = cb_obj.range
        var x = data['x']
        var y = data['y']
        var pow = (Math.log(y[100])/Math.log(x[100]))
        console.log(pow)
        var delta = (f[1] - f[0])/x.length
        for (var i = 0; i < x.length; i++) {
            x[i] = delta*i + f[0]
            y[i] = Math.pow(x[i], pow)
        }
        source.change.emit();
    """)


slider = Slider(start=0, end=5, step=0.1, value=1, title="Bokeh Slider - Power")
slider.js_on_change('value', callback_single)

ion_range_slider = IonRangeSlider(start=0.01, end=0.99, step=0.01, range=(min(x), max(x)),
    title='Ion Range Slider - Range', callback_policy='continuous', callback=callback_ion)

layout = column(plot, slider, ion_range_slider)
show(layout)
示例#17
0
lines'''

]

def xs(cat):
    return [(cat, -0.3), (cat, -0.1), (cat, 0.1), (cat, 0.3)]

def ys(cat):
    return [cat] * 4

renderers = {}
i = 0

for a in aligns:
    for b in baselines:
        r = p.text(xs(a), ys(b), texts, text_align=a, text_baseline=b,
                   text_font_size="14px", text_line_height=1.2)
        renderers["r" + str(i)] = r
        i += 1

slider = Slider(title="Text Angle", start=0, end=45, step=1, value=0)
slider.js_on_change('value', CustomJS(args=renderers, code="""
    var rs = [r0, r1, r2 , r3, r4, r5, r6, r7, r8];
    for (var i = 0; i < 9; i++) {
        rs[i].glyph.angle = {value: cb_obj.value, units: "deg"}
    }
"""))

output_file("text.html")
show(column(p, slider))
示例#18
0
def Rips_Filter_Bifiltration(filtered_points,
                             radius_range,
                             palette="Viridis256",
                             FilterName="Filter",
                             maxind: int = None,
                             dim: int = None):
    if maxind is None:
        maxind = 5
    if dim is None:
        dim = 0

    points = filtered_points[:, :2]
    filter = filtered_points[:, 2]

    alpha = np.ones(filter.shape) * 0.3
    exp_cmap = LinearColorMapper(palette=palette,
                                 low=radius_range[0],
                                 high=radius_range[1])

    source = ColumnDataSource(
        data=dict(x=points[:, 0],
                  y=points[:, 1],
                  sizes=(radius_range[0] + radius_range[1]) / 4 *
                  np.ones(points.shape[0]),
                  filter=filter,
                  alpha=alpha))

    vline = ColumnDataSource(
        data=dict(c=[radius_range[0]], y=[0], angle=[np.pi / 2]))
    hline = ColumnDataSource(data=dict(s=[radius_range[0]], x=[0], angle=[0]))

    filter_plot = figure(title='Filtration',
                         plot_width=360,
                         plot_height=430,
                         min_border=0,
                         toolbar_location=None,
                         match_aspect=True)

    glyph = Circle(x="x",
                   y="y",
                   radius="sizes",
                   line_color="black",
                   fill_color={
                       'field': 'filter',
                       'transform': exp_cmap
                   },
                   fill_alpha="alpha",
                   line_width=1,
                   line_alpha="alpha")

    filter_plot.add_glyph(source, glyph)
    filter_plot.add_layout(
        ColorBar(color_mapper=exp_cmap,
                 location=(0, 0),
                 orientation="horizontal"), "below")

    rips_callback = CustomJS(args=dict(source=source, hline=hline),
                             code="""
    var data = source.data;
    var s = cb_obj.value
    var sizes = data['sizes']


    for (var i = 0; i < sizes.length; i++) {
        sizes[i] = s/2
    }
    var hdata = hline.data;
    var step = hdata['s']
    step[0] = s
    hline.change.emit();
    source.change.emit();
    """)

    filter_callback = CustomJS(args=dict(source=source, vline=vline),
                               code="""
    var data = source.data;
    var c = cb_obj.value
    var alpha = data['alpha']
    var filter = data['filter']

    for (var i = 0; i<filter.length; i++){
        if(filter[i]>c){
        alpha[i] = 0
        }
        if(filter[i]<=c){
        alpha[i] = 0.3
        }
    }

    var vdata = vline.data;
    var step = vdata['c']
    step[0] = c
    vline.change.emit();
    source.change.emit();
    """)

    rips_slider = Slider(start=radius_range[0],
                         end=radius_range[1],
                         value=(radius_range[0] + radius_range[1]) / 2,
                         step=(radius_range[1] - radius_range[0]) / 100,
                         title="Rips",
                         orientation="vertical",
                         height=300,
                         direction="rtl",
                         margin=(10, 40, 10, 60))

    rips_slider.js_on_change('value', rips_callback)

    filter_slider = Slider(start=radius_range[0],
                           end=radius_range[1],
                           value=radius_range[1],
                           step=(radius_range[1] - radius_range[0]) / 100,
                           title=FilterName,
                           orientation="horizontal",
                           aspect_ratio=10,
                           width_policy="auto",
                           direction="ltr",
                           width=300,
                           margin=(10, 10, 10, 40))

    filter_slider.js_on_change('value', filter_callback)

    # Run Rivet and Landscape Computation #

    computed_data = Compute_Rivet(filtered_points,
                                  resolution=50,
                                  dim=dim,
                                  RipsMax=radius_range[1])
    multi_landscape = multiparameter_landscape(
        computed_data,
        grid_step_size=(radius_range[1] - radius_range[0]) / 100,
        bounds=[[radius_range[0], radius_range[0]],
                [radius_range[1], radius_range[1]]],
        maxind=maxind)

    TOOLTIPS = [(FilterName, "$x"), ("Radius", "$y"),
                ("Landscape_Value", "@image")]

    landscape_plots = plot_multiparameter_landscapes(
        multi_landscape,
        indices=[1, maxind],
        TOOLTIPS=TOOLTIPS,
        x_axis_label=FilterName,
        y_axis_label="Rips Parameter")

    for plot in landscape_plots.children:
        plot.ray(x="c",
                 y="y",
                 length="y",
                 angle="angle",
                 source=vline,
                 color='white',
                 line_width=2,
                 alpha=0.5)
        plot.ray(x="x",
                 y="s",
                 length="x",
                 angle="angle",
                 source=hline,
                 color='white',
                 line_width=2,
                 alpha=0.5)

    layout = column(row(rips_slider, column(filter_plot, filter_slider)),
                    row(landscape_plots),
                    sizing_mode="scale_both")

    return layout
示例#19
0
This example demonstrates how ``CustomJS`` callbacks react to user
interaction events.

.. bokeh-example-metadata::
    :apis: bokeh.layouts.column, bokeh.models.callbacks.CustomJS
    :refs: :ref:`userguide_interaction_jscallbacks` > :ref:`userguide_interaction_jscallbacks_customjs_interactions`
    :keywords: javascript callback

'''
from bokeh.io import output_file, show
from bokeh.layouts import column
from bokeh.models import CustomJS, Div, Slider

para = Div(
    text="<h1>Slider Values:</h1><p>Slider 1: 0<p>Slider 2: 0<p>Slider 3: 0")

s1 = Slider(title="Slider 1 (Continuous)", start=0, end=1000, value=0, step=1)
s2 = Slider(title="Slider 3 (Mouse Up)", start=0, end=1000, value=0, step=1)

callback = CustomJS(args=dict(para=para, s1=s1, s2=s2),
                    code="""
    para.text = "<h1>Slider Values</h1><p>Slider 1: " + s1.value  + "<p>Slider 2: " + s2.value
""")

s1.js_on_change('value', callback)
s2.js_on_change('value_throttled', callback)

output_file('slider_callback_policy.html')

show(column(s1, s2, para))
示例#20
0
def Rips_Filtration(points, radius_range):
    rips = Rips(maxdim=1, thresh=radius_range[1], verbose=False)
    barcodes = rips.transform(points)
    H_0_Bars = barcodes[0]
    H_1_Bars = barcodes[1]

    # Plotting Parameters

    H_0_color = '#1d07ad'
    H_1_color = '#009655'
    vertical_line_color = "#FB8072"
    bar_line_width = 2
    circle_opacity = 0.2
    circle_color = "green"
    circle_line_color = "black"

    source = ColumnDataSource(data=dict(x=points[:, 0],
                                        y=points[:, 1],
                                        sizes=radius_range[0] / 2 * np.ones(points.shape[0]),
                                        )
                              )
    vline = ColumnDataSource(data=dict(s=[radius_range[0]],
                                       y=[0],
                                       angle=[np.pi / 2]
                                       )
                             )

    filt_plot = figure(title='Filtration',
                       plot_width=300,
                       plot_height=300,
                       aspect_ratio="auto",
                       height_policy="auto",
                       min_border=0,
                       toolbar_location=None,
                       match_aspect=True)
    glyph = Circle(x="x", y="y", radius="sizes", line_color=circle_line_color, fill_color=circle_color,
                   fill_alpha=circle_opacity, line_width=1)

    filt_plot.add_glyph(source, glyph)

    callback = CustomJS(args=dict(source=source, vline=vline), code="""
    var data = source.data;
    var s = cb_obj.value
    var sizes = data['sizes']
    for (var i = 0; i < sizes.length; i++) {
        sizes[i] = s/2
    }
    var vdata = vline.data;
    var step = vdata['s']
    step[0] = s
    vline.change.emit();
    source.change.emit();
    """)

    barcode_plot = figure(title='Barcode',
                          plot_width=800,
                          plot_height=200,
                          min_border=0,
                          aspect_ratio="auto",
                          height_policy="auto",
                          toolbar_location=None,
                          x_axis_label='Filtration Value',
                          x_range=(radius_range[0], radius_range[1]))

    lscape_plot = figure(title='Landscapes',
                         plot_width=800,
                         plot_height=200,
                         min_border=0,
                         aspect_ratio="auto",
                         height_policy="auto",
                         toolbar_location=None,
                         x_axis_label='Filtration Value',
                         x_range=(radius_range[0], radius_range[1]))

    for bar in range(len(H_0_Bars)):
        if H_0_Bars[bar, 1] < radius_range[1]:
            barcode_plot.line([H_0_Bars[bar, 0], H_0_Bars[bar, 1]], [bar / len(H_0_Bars), bar / len(H_0_Bars)],
                              legend_label='H0 Bars', color=H_0_color, line_width=bar_line_width)
        else:
            barcode_plot.line([H_0_Bars[bar, 0], radius_range[1]], [bar / len(H_0_Bars), bar / len(H_0_Bars)],
                              legend_label='H0 Bars', color=H_0_color, line_width=bar_line_width)
    for bar in range(len(H_1_Bars)):
        if H_1_Bars[bar, 1] < radius_range[1]:
            barcode_plot.line([H_1_Bars[bar, 0], H_1_Bars[bar, 1]],
                              [3 / 2 + bar / len(H_1_Bars), 3 / 2 + bar / len(H_1_Bars)],
                              legend_label='H1 Bars', color=H_1_color, line_width=bar_line_width)
        else:
            barcode_plot.line([H_1_Bars[bar, 0], radius_range[1]],
                              [3 / 2 + bar / len(H_1_Bars), 3 / 2 + bar / len(H_1_Bars)],
                              legend_label='H1 Bars', color=H_1_color, line_width=bar_line_width)

    barcode_plot.ray(x="s", y="y", length="y", angle="angle", source=vline, color="#FB8072", line_width=bar_line_width)

    barcode_plot.yaxis.major_tick_line_color = None  # turn off y-axis major ticks
    barcode_plot.yaxis.minor_tick_line_color = None  # turn off y-axis minor ticks
    barcode_plot.yaxis.major_label_text_font_size = '0pt'  # preferred method for removing tick labels
    barcode_plot.legend.location = "bottom_right"
    H0rivet_barcode = ripser_to_rivet_bcode(H_0_Bars)
    L = compute_landscapes(H0rivet_barcode)

    for k in range(len(L.landscapes)):
        n = np.shape(L.landscapes[k].critical_points)[0]
        x = L.landscapes[k].critical_points[1:n, 0]
        y = L.landscapes[k].critical_points[1:n, 1]
        if k < 2:
            lscape_plot.line(x=x, y=y, color=H_0_color, line_alpha=1 / (k + 1), line_width=bar_line_width,
                             legend_label='H0: k =' + str(k + 2), muted_color=vertical_line_color, muted_alpha=1)
        else:
            lscape_plot.line(x=x, y=y, color=H_0_color, line_alpha=1 / (k + 1), line_width=bar_line_width)

    H_1_rivet_barcode = ripser_to_rivet_bcode(H_1_Bars)
    L = compute_landscapes(H_1_rivet_barcode)

    for k in range(len(L.landscapes)):
        n = np.shape(L.landscapes[k].critical_points)[0]
        x = L.landscapes[k].critical_points[1:n, 0]
        y = L.landscapes[k].critical_points[1:n, 1]
        if k < 2:
            lscape_plot.line(x=x, y=y, color=H_1_color, line_alpha=1 / (k + 1), line_width=bar_line_width,
                             legend_label='H1: k =' + str(k + 1), muted_color=vertical_line_color, muted_alpha=1)
        else:
            lscape_plot.line(x=x, y=y, color=H_1_color, line_alpha=1 / (k + 1), line_width=bar_line_width)
    lscape_plot.legend.location = "top_right"
    lscape_plot.legend.click_policy = "mute"

    lscape_plot.ray(x="s", y="y", length="y", angle="angle",
                    source=vline, color=vertical_line_color, line_width=bar_line_width)

    slider = Slider(start=radius_range[0], end=radius_range[1], value=radius_range[0],
                    step=(radius_range[1] - radius_range[0]) / 100, title="Rips Parameter", aspect_ratio=20)
    slider.js_on_change('value', callback)

    layout = column(row(filt_plot), slider, barcode_plot, lscape_plot, sizing_mode="scale_both")

    return layout
示例#21
0
def get_dashboard(local_data=False):
    """
    Assemble the dashboard. Define the layout, controls and 
    its callbacks, as well as data sources.
    """
    # Load data
    video_data = {}
    if local_data:
        data = pd.json_normalize(
            pd.read_json('data/channel/processed_data.json')['items'])
        with open('data/channel/processed_data.json', 'r') as f:
            data = json.load(f)
            video_data = data['items']
    else:
        video_data = handle_channel_data.get_data_firebase()['items']

    # X axis categories
    x_axis_map = {
        "Climber": "climber",
        "Zone": "zone",
        "Grade": "grade",
    }
    # Y axis categories
    y_axis_map = {
        "Count": "count",
        "Views": "viewCount",
        # "Favourites": "favoriteCount",
        "Likes": "likeCount",
        "Dislikes": "dislikeCount",
        "Comments": "commentCount"
    }

    # get ready to plot data
    barchart_data = prepare_barchart_data(video_data, x_axis_map)

    # html template to place the plots
    desc = Div(text=open(join(dirname(__file__),
                              "templates/stats.html")).read(),
               sizing_mode="stretch_width")

    # initial data source fill
    data_to_plot = barchart_data['grade']['raw']

    od = collections.OrderedDict(
        sorted(data_to_plot.items(), key=lambda x: x[0]))

    x_to_plot = np.array([key for key, _ in od.items()])
    y_to_plot = np.array([val['count'] for _, val in od.items()])
    source = ColumnDataSource(data=dict(x=x_to_plot, y=y_to_plot))
    # initial data
    x_init = x_to_plot[0:NUM_RESULTS]
    y_init = y_to_plot[0:NUM_RESULTS]

    # Create Input controls
    checkbox_limit_results = CheckboxGroup(
        labels=["Show only first 50 results"], active=[0])

    label_slider = Slider(start=0,
                          end=90,
                          value=90,
                          step=1,
                          title="Label Angle")

    range_slider = RangeSlider(title="Value Range",
                               start=0,
                               end=max(y_to_plot),
                               value=(0, max(y_to_plot)),
                               step=1)

    min_year = Slider(title="From", start=2015, end=2020, value=2015, step=1)

    max_year = Slider(title="To", start=2015, end=2020, value=2020, step=1)

    sort_order = RadioButtonGroup(
        labels=["Alphabetically", "Decreasing", "Increasing"], active=0)
    x_axis = Select(title="X Axis",
                    options=sorted(x_axis_map.keys()),
                    value="Grade")

    y_axis = Select(title="Y Axis",
                    options=sorted(y_axis_map.keys()),
                    value="Count")

    checkbox = CheckboxGroup(
        labels=["Show ratio with respect to number of videos"], active=[])

    # show number of categories
    x_count_source = ColumnDataSource(
        data=dict(x_count=[len(x_init)], category=[x_axis.value]))

    columns = [
        TableColumn(field="category", title="Category"),
        TableColumn(field="x_count", title="Count"),
    ]

    x_count_data_table = DataTable(source=x_count_source,
                                   columns=columns,
                                   width=320,
                                   height=280)

    # Generate the actual plot
    # p = figure(x_range=x_to_plot, y_range=(0, max(y_to_plot)), plot_height=250, title="{} {}".format(x_axis.value, y_axis.value),
    #            toolbar_location="above")
    p = figure(x_range=x_init,
               y_range=(0, max(y_init)),
               plot_height=250,
               title="{} {}".format(x_axis.value, y_axis.value),
               toolbar_location="above")

    # Fill it with data and format it
    p.vbar(x='x', top='y', width=0.9, source=source)
    p.xaxis.major_label_orientation = math.pi / 2
    p.add_tools(HoverTool(tooltips=[("name", "@x"), ("count", "@y")]))

    # Controls
    controls = [
        checkbox_limit_results, range_slider, min_year, max_year, sort_order,
        x_axis, y_axis, checkbox, label_slider, x_count_data_table
    ]

    # Callbacks for controls
    label_callback = CustomJS(args=dict(axis=p.xaxis[0]),
                              code="""
        axis.major_label_orientation = cb_obj.value * Math.PI / 180;
        """)
    label_slider.js_on_change('value', label_callback)

    # limit checkbox
    checkbox_limit_results_callback = CustomJS(
        args=dict(source=source,
                  x_source=x_count_source,
                  o_data=barchart_data,
                  sort_order=sort_order,
                  x_axis_map=x_axis_map,
                  x_axis=x_axis,
                  y_axis_map=y_axis_map,
                  y_axis=y_axis,
                  range_slider=range_slider,
                  checkbox=checkbox,
                  fig=p,
                  title=p.title),
        code=SORT_FUNCTION + JS_NUM_RESULTS + """
            var data = o_data[x_axis_map[x_axis.value]];
            var x = data['x'];
            var y = data['y'];
            var apply_limit = cb_obj.active.length > 0;
            var is_ratio = checkbox.active.length > 0;
            title.text = x_axis.value.concat(" ", y_axis.value);
            // Sort data
            var sorted_data = sortData(data['raw'], sort_order.active, y_axis_map[y_axis.value], is_ratio);
            var new_y = [];
            var new_x = [];
            var final_x = [];
            var final_y = [];
            for (var i = 0; i < x.length; i++) {
                if (apply_limit)
                {
                    if(sorted_data[i][1] >= range_slider.value[0] && sorted_data[i][1] <= range_slider.value[1]) 
                    {
                        new_x.push(sorted_data[i][0]);
                        new_y.push(sorted_data[i][1]);
                    } 
                } else {
                    new_x.push(sorted_data[i][0]);
                    new_y.push(sorted_data[i][1]);                    
                }
            }
            if (apply_limit) { 
                final_x = new_x.slice(0, num_results);
                final_y = new_y.slice(0, num_results);
                window.should_update_range = false;
            } else {
                final_x = new_x;
                final_y = new_y;
                window.should_update_range = true;
            }
            x_source.data['x_count'] = [final_x.length];
            x_source.data['category'] = [x_axis.value];
            x_source.change.emit();
            source.data['x'] = new_x;
            source.data['y'] = new_y;
            source.change.emit();
            fig.x_range.factors = [];
            fig.x_range.factors = final_x;
            if (Array.isArray(new_y) && new_y.length) {
                // range init and end cannot have same value
                var range_end = Math.max.apply(Math, new_y);
                if (range_end == 0 || range_end == -Infinity) {
                    range_end = 1;
                }
                range_slider.value = [0, Math.max.apply(Math, final_y)]; 
                range_slider.end = range_end;
                fig.y_range.end = Math.max.apply(Math, final_y);
                fig.change.emit();
            }        
        """)
    checkbox_limit_results.js_on_change('active',
                                        checkbox_limit_results_callback)

    # ratio checkbox
    checkbox_callback = CustomJS(args=dict(
        source=source,
        x_source=x_count_source,
        o_data=barchart_data,
        sort_order=sort_order,
        x_axis_map=x_axis_map,
        x_axis=x_axis,
        y_axis_map=y_axis_map,
        y_axis=y_axis,
        range_slider=range_slider,
        checkbox_limit_results=checkbox_limit_results,
        fig=p,
        title=p.title),
                                 code=SORT_FUNCTION + JS_NUM_RESULTS + """
            var data = o_data[x_axis_map[x_axis.value]];
            var x = data['x'];
            var y = data['y'];
            var is_ratio = cb_obj.active.length > 0;
            title.text = x_axis.value.concat(" ", y_axis.value);
            if (is_ratio)
            {
                title.text = x_axis.value.concat(" ", y_axis.value, " per video");   
            }
            // Sort data
            var sorted_data = sortData(data['raw'], sort_order.active, y_axis_map[y_axis.value], is_ratio);
            var new_y = [];
            var new_x = [];
            var final_x = [];
            var final_y = [];
            for (var i = 0; i < x.length; i++) {
                if (checkbox_limit_results.active.length <= 0)
                {
                    if(sorted_data[i][1] >= range_slider.value[0] && sorted_data[i][1] <= range_slider.value[1]) 
                    {
                        new_x.push(sorted_data[i][0]);
                        new_y.push(sorted_data[i][1]);
                    } 
                } else {
                    new_x.push(sorted_data[i][0]);
                    new_y.push(sorted_data[i][1]);                    
                }
            }
            if (checkbox_limit_results.active.length > 0) { 
                final_x = new_x.slice(0, num_results);
                final_y = new_y.slice(0, num_results);
                window.should_update_range = false;
            } else {
                final_x = new_x;
                final_y = new_y;
                window.should_update_range = true;
            }
            x_source.data['x_count'] = [final_x.length];
            x_source.data['category'] = [x_axis.value];
            x_source.change.emit();
            source.data['x'] = new_x;
            source.data['y'] = new_y;
            source.change.emit();
            fig.x_range.factors = [];
            fig.x_range.factors = final_x;
            if (Array.isArray(new_y) && new_y.length) {
                // range init and end cannot have same value
                var range_end = Math.max.apply(Math, new_y);
                if (range_end == 0 || range_end == -Infinity) {
                    range_end = 1;
                }
                range_slider.value = [0, Math.max.apply(Math, final_y)]; 
                range_slider.end = range_end;
                fig.y_range.end = Math.max.apply(Math, final_y);
                fig.change.emit();
            }
        """)
    checkbox.js_on_change('active', checkbox_callback)

    # range slider
    range_callback = CustomJS(args=dict(
        source=source,
        x_source=x_count_source,
        o_data=barchart_data,
        sort_order=sort_order,
        x_axis_map=x_axis_map,
        x_axis=x_axis,
        y_axis_map=y_axis_map,
        y_axis=y_axis,
        checkbox=checkbox,
        checkbox_limit_results=checkbox_limit_results,
        fig=p),
                              code=SORT_FUNCTION + """
            if (window.should_update_range == true) {
                var data = o_data[x_axis_map[x_axis.value]];
                var x = data['x'];
                var y = data['y'];
                var is_ratio = checkbox.active.length > 0;
                // Sort data
                var sorted_data = sortData(data['raw'], sort_order.active, y_axis_map[y_axis.value], is_ratio);
                var new_y = [];
                var new_x = [];
                for (var i = 0; i < x.length; i++) {
                    if (checkbox_limit_results.active.length <= 0)
                    {
                        if (sorted_data[i][1] >= cb_obj.value[0] && sorted_data[i][1] <= cb_obj.value[1]) 
                        {
                            new_x.push(sorted_data[i][0]);
                            new_y.push(sorted_data[i][1]);
                        }
                    } else {
                        new_x.push(sorted_data[i][0]);
                        new_y.push(sorted_data[i][1]);                        
                    }
                }
                x_source.data['x_count'] = [new_x.length];
                x_source.data['category'] = [x_axis.value];
                x_source.change.emit();
                source.data['x'] = new_x;
                source.data['y'] = new_y;
                source.change.emit();
                fig.x_range.factors = [];
                fig.x_range.factors = new_x;
                if (Array.isArray(new_y) && new_y.length) {
                    fig.y_range.end = Math.max.apply(Math, new_y);
                }
            } else {
                window.should_update_range = true;
            }
        """)
    range_slider.js_on_change('value', range_callback)

    # variable to group data
    x_axis_callback = CustomJS(args=dict(
        source=source,
        x_source=x_count_source,
        o_data=barchart_data,
        x_axis_map=x_axis_map,
        y_axis_map=y_axis_map,
        y_axis=y_axis,
        range_slider=range_slider,
        sort_order=sort_order,
        checkbox=checkbox,
        checkbox_limit_results=checkbox_limit_results,
        fig=p,
        title=p.title),
                               code=SORT_FUNCTION + JS_NUM_RESULTS + """
            title.text = cb_obj.value.concat(" ", y_axis.value);
            var data = o_data[x_axis_map[cb_obj.value]];
            var x = data['x'];
            var y = data['y'];
            var is_ratio = checkbox.active.length > 0;
            if (is_ratio)
            {
                title.text = title.text.concat(" per video");   
            }
            var sorted_data = sortData(data['raw'], sort_order.active, y_axis_map[y_axis.value], is_ratio);
            var new_y = [];
            var new_x = [];
            var final_x = [];
            var final_y = [];
            for (var i = 0; i < x.length; i++) {
                new_x.push(sorted_data[i][0]);
                new_y.push(sorted_data[i][1]);
            }
            if (checkbox_limit_results.active.length > 0) {
                final_x = new_x.slice(0, num_results);
                final_y = new_y.slice(0, num_results);
                window.should_update_range = false;
            } else {
                final_x = new_x;
                final_y = new_y;
                window.should_update_range = true;
            }
            x_source.data['x_count'] = [final_x.length];
            x_source.data['category'] = [cb_obj.value];
            x_source.change.emit();
            source.data['x'] = new_x;
            source.data['y'] = new_y;
            source.change.emit();
            fig.x_range.factors = [];
            fig.x_range.factors = final_x;
            if (new_y && Array.isArray(new_y) && new_y.length) {
                // range init and end cannot have same value
                var range_end = Math.max.apply(Math, new_y);
                if (range_end == 0 || range_end == -Infinity) {
                    range_end = 1;
                }
                range_slider.value = [0, Math.max.apply(Math, final_y)]; 
                range_slider.end = range_end;
                fig.y_range.end = Math.max.apply(Math, final_y);
                fig.change.emit();
            }
        """)

    x_axis.js_on_change('value', x_axis_callback)

    # variable to group data
    y_axis_callback = CustomJS(args=dict(
        source=source,
        x_source=x_count_source,
        o_data=barchart_data,
        x_axis_map=x_axis_map,
        x_axis=x_axis,
        y_axis_map=y_axis_map,
        range_slider=range_slider,
        sort_order=sort_order,
        checkbox=checkbox,
        checkbox_limit_results=checkbox_limit_results,
        fig=p,
        title=p.title),
                               code=SORT_FUNCTION + JS_NUM_RESULTS + """
            title.text = x_axis.value.concat(" ", cb_obj.value);
            var data = o_data[x_axis_map[x_axis.value]];
            var x = data['x'];
            var y = data['y'];
            var is_ratio = checkbox.active.length > 0;
            if (is_ratio)
            {
                title.text = x_axis.value.concat(" ", cb_obj.value, " per video");   
            }
            var sorted_data = sortData(data['raw'], sort_order.active, y_axis_map[cb_obj.value], is_ratio);
            var new_y = [];
            var new_x = [];
            var final_x = [];
            var final_y = [];
            for (var i = 0; i < x.length; i++) {
                new_x.push(sorted_data[i][0]);
                new_y.push(sorted_data[i][1]);
            }
            if (checkbox_limit_results.active.length > 0) {
                final_x = new_x.slice(0, num_results);
                final_y = new_y.slice(0, num_results);
                window.should_update_range = false;
            } else {
                final_x = new_x;
                final_y = new_y;
                window.should_update_range = true;
            }
            x_source.data['x_count'] = [final_x.length];
            x_source.data['category'] = [x_axis.value];
            x_source.change.emit();
            source.data['x'] = new_x;
            source.data['y'] = new_y;
            source.change.emit();
            fig.x_range.factors = [];
            fig.x_range.factors = final_x;
            if (new_y && Array.isArray(new_y) && new_y.length) {
                // range init and end cannot have same value
                var range_end = Math.max.apply(Math, new_y);
                if (range_end == 0 || range_end == -Infinity) {
                    range_end = 1;
                }
                range_slider.value = [0, Math.max.apply(Math, final_y)]; 
                range_slider.end = range_end;
                fig.y_range.end = Math.max.apply(Math, final_y);
            }
        """)

    y_axis.js_on_change('value', y_axis_callback)

    # sort order control
    sort_order_callback = CustomJS(args=dict(
        source=source,
        x_source=x_count_source,
        o_data=barchart_data,
        x_axis_map=x_axis_map,
        x_axis=x_axis,
        y_axis_map=y_axis_map,
        y_axis=y_axis,
        range_slider=range_slider,
        checkbox=checkbox,
        checkbox_limit_results=checkbox_limit_results,
        fig=p),
                                   code=SORT_FUNCTION + JS_NUM_RESULTS + """
            var data = o_data[x_axis_map[x_axis.value]];
            var x = data['x'];
            var y = data['y'];
            // Sort data
            var is_ratio = checkbox.active.length > 0;
            var sorted_data = sortData(data['raw'], cb_obj.active, y_axis_map[y_axis.value], is_ratio);
            var new_y = [];
            var new_x = [];
            var final_x = [];
            var final_y = [];
            // push data if it lies inside range
            for (var i = 0; i < x.length; i++) {
                    if (checkbox_limit_results.active.length <= 0)
                    {
                        if (sorted_data[i][1] >= cb_obj.value[0] && sorted_data[i][1] <= cb_obj.value[1]) 
                        {
                            new_x.push(sorted_data[i][0]);
                            new_y.push(sorted_data[i][1]);
                        }
                    } else {
                        new_x.push(sorted_data[i][0]);
                        new_y.push(sorted_data[i][1]);                        
                    }
            }
            if (checkbox_limit_results.active.length > 0)
            { 
                final_x = new_x.slice(0, 50);
                final_y = new_y.slice(0, 50);
                window.should_update_range = false;
            } else {
                final_x = new_x;
                final_y = new_y;
                window.should_update_range = true;
            }
            x_source.data['x_count'] = [final_x.length];
            x_source.data['category'] = [x_axis.value];
            x_source.change.emit();
            source.data['x'] = new_x;
            source.data['y'] = new_y;
            source.change.emit();
            fig.x_range.factors = [];
            fig.x_range.factors = final_x;
            if (new_y && Array.isArray(new_y) && new_y.length) {
                // range init and end cannot have same value
                var range_end = Math.max.apply(Math, new_y);
                if (range_end == 0 || range_end == -Infinity) {
                    range_end = 1;
                }
                range_slider.value = [0, Math.max.apply(Math, final_y)]; 
                range_slider.end = range_end;
                fig.y_range.end = Math.max.apply(Math, final_y);
                fig.change.emit();
            }
        """)

    sort_order.js_on_change('active', sort_order_callback)

    # Define layout
    inputs = column(*controls, width=320, height=1000)
    inputs.sizing_mode = "fixed"
    l = layout([
        [desc],
        [inputs, p],
    ], sizing_mode="scale_both")

    return l
p.patches('x', 'y', source=source, fill_alpha=0.5, fill_color='#8E0D3F', line_color="white", line_width=0.5)

year_slider = Slider(start=2012, end=2017, value=2012, step=1, title="Year")

callback = CustomJS(args=dict(source=source, year=year_slider, master=income_master_list), code="""
    var data = source.data;
    var yr = year.value;
    const x = data['x'];
    const y = data['y'];
    const names = data['name'];
    data['income'] = master[yr - 2012];
    
    source.change.emit()
""")

year_slider.js_on_change('value', callback)

layout = row(
    p,
    column(year_slider)
)

hover = HoverTool()
hover.point_policy = "follow_mouse"
hover.tooltips = """
<div>
    <h3>@name County</h3>
    <div><strong>Median Income: </strong>@income</div>
    <div><strong>(Long, Lat): </strong>($x, $y)</div>
    <div></div>
</div>
示例#23
0
from bokeh.io import show
from bokeh.models import Slider, CustomJS
from bokeh.layouts import row

from vtk_js import VtkJs

vtkjs = VtkJs(resolution=10, width=400, height=400)
slider = Slider(value=10, start=1, end=100, step=1)
cb = CustomJS(args={'vtkjs': vtkjs},
              code="""
vtkjs.resolution = cb_obj.value
""")
slider.js_on_change('value', cb)
show(row([vtkjs, slider]))
示例#24
0
def plot(y: list,
         x: list = None,
         label: list = None,
         width: int = 400,
         height: int = 400,
         gain: float = 0.4,
         margin_x: int = 1,
         title: str = "graph",
         script_name: str = "",
         slider_partitions: int = None,
         multiple_axes=False):
    """Plots that represent data with sound and can be checked interactively

    You can interactively check the data in graph form by moving the mouse cursor.
    When you enter or leave the graph image, you will be notified by voice.
    Also, when you move the mouse left or right on the graph image,
    the y-axis value corresponding to that location will be expressed with a high or low tone.
    A single click will read out the value corresponding to that location.
    Also, double-clicking switches the group according to the label specified as an option.

    Parameters
    ----------
    y : list
        A list of values to be graphed.
    x : list
        A list of x-axis values corresponding to y-axis values.
        If not specified, it is substituted by the value of the equal interval. Optional.
    label : list
        A list of grouping numbers for each value, which must start with zero.
        You can compare the graph data by sound, switching between each number. Optional.
    width : int
        Width of the graph image (in pixels). Optional.
    height : int
        Height of the graph image (in pixels). Optional.
    title: str
        Graph name to be read out. Optional.
    multiple_axes: bool
        Set to True if you want each label to have a separate y-axis. Optional.

    Examples
    --------
    >>> plot([0, 1, 2])
    <IPython.core.display.HTML object>
    >>> plot(x=[0, 1, 2], y=[4, 5, 6], label=[0, 0, 1])
    <IPython.core.display.HTML object>
    """

    if type(y) == np.ndarray:
        y = y.tolist()

    if type(x) == np.ndarray:
        x = x.tolist()
    elif x == None:
        x = np.arange(len(y)).tolist()

    if type(label) == np.ndarray:
        label = label.astype(int).tolist()
    elif label == None:
        label = np.zeros_like(y).astype(int).tolist()

    if label:
        assert max(label) < len(__COLORS), "max label must be lower {}".format(
            len(__COLORS))
        assert max(label) + 1 == len(set(
            label)), "label should be in {} because max label is {}.".format(
                list(range(max(label) + 1)), max(label))

    if script_name == "":
        __set_context()
        output_notebook()

    plot = figure(plot_width=width,
                  plot_height=height,
                  tools="",
                  toolbar_location=None)
    colors = [__COLORS[c] for c in label]

    if multiple_axes:
        assert max(label) == 1, "The number of labels must be two kinds"

        multi_axes_str = "true"
        y_ranges = {}
        for l in range(max(label) + 1):
            __x = np.array(x)[np.array(label) == l].tolist()
            __y = np.array(y)[np.array(label) == l].tolist()
            __c = np.array(colors)[np.array(label) == l].tolist()
            plot.scatter(__x,
                         __y,
                         line_color=__c,
                         fill_color=__c,
                         y_range_name=str(l))
            if l == 1:
                plot.add_layout(LinearAxis(y_range_name=str(l)), 'right')
            y_ranges[str(l)] = Range1d(start=min(__y) - 1, end=max(__y) + 1)

        plot.extra_y_ranges = y_ranges

    else:
        multi_axes_str = "false"
        plot.scatter(x, y, line_color=colors, fill_color=colors)

    sound_js = """
    const multiAxes = %s;
    %s
    if(diff[nearestIdx] > marginX) {
        return;
    }

    const gain = %s; // max: 1.0
    osc.type = 'triangle'; // sine, square, sawtooth, triangle
    osc.frequency.value = 261.626 + (nearestY - minY) / (maxY - minY) * 261.626 // Hz
    audioGain.gain.linearRampToValueAtTime(gain, audioContext.currentTime + 0.2); // atack
    audioGain.gain.setTargetAtTime(0, audioContext.currentTime + 0.2, 0.5); // decay, sustain

    let pan = (nearestX - minX) / (maxX - minX) * 2 - 1;
    panNode.pan.value = pan;  // left:-1 ~ right:1
    """ % (multi_axes_str, __FIND_NEAREST_JS, gain)

    # Mouse hover on plot
    hover_code = """
    let marginX = %s;
    let position = cb_data.geometry.x;
    %s
    """ % (margin_x, sound_js)

    callback = CustomJS(args={"x": x, "y": y, "label": label}, code=hover_code)
    plot.add_tools(HoverTool(tooltips=None, callback=callback))

    # Single tap on plot
    tap_code = """
    let position = cb_obj.x;
    const multiAxes = %s;
    %s
    %s
    """ % (multi_axes_str, __FIND_NEAREST_JS,
           __speak_js("`X is ${nearestX}. Y is ${nearestY}`"))

    plot.js_on_event(
        events.Tap,
        CustomJS(args={
            "x": x,
            "y": y,
            "label": label
        }, code=tap_code))

    if len(set(label)) > 1:
        # Double tap on plot
        double_tap_code = """
        oscTarget = (oscTarget + 1) %% (maxLabel + 1);
        %s
        """ % (__speak_js("`label ${oscTarget} is selected`"))
        plot.js_on_event(
            events.DoubleTap,
            CustomJS(args={"maxLabel": max(label)}, code=double_tap_code))

    # Enter or leave on plot
    read_label = (max(label) > 0)
    plot.js_on_event(events.MouseEnter, __speak_inout(title, True, read_label))
    plot.js_on_event(events.MouseLeave, __speak_inout(title, False,
                                                      read_label))

    # slider for keyboard interaction
    sliders = []
    for l in range(max(label) + 1):
        __x = np.array(x)[np.array(label) == l].tolist()

        if slider_partitions is None:
            slider_partitions = np.min([len(__x) - 1, 30])
            if slider_partitions == 30:
                print(
                    "The number of slider partitions has been reduced to 30 as the default limit. Please set slider_partitions as an argument if necessary."
                )

        slider_start = np.min(__x)
        slider_end = np.max(__x)
        if slider_start == slider_end:
            slider_end += 1
        slider_step = (slider_end - slider_start) / slider_partitions

        slider_code = """
        oscTarget = target;
        let marginX = %s;
        let position = slider.value;
        %s
        setTimeout(function(){%s}, 3000);
        """ % (slider_step, sound_js,
               __speak_js("`X is ${nearestX}. Y is ${nearestY}`"))

        slider = Slider(start=slider_start,
                        end=slider_end,
                        value=slider_start,
                        step=slider_step,
                        title="label {}".format(l))
        slider.js_on_change(
            'value',
            CustomJS(args={
                "x": x,
                "y": y,
                "label": label,
                "slider": slider,
                "target": l
            },
                     code=slider_code))
        sliders.append(slider)

    # layout
    message1 = Div(text="""<h2>output of audio plot lib</h2>""")
    message2 = Div(
        text=
        """<p>There is a graph and a series of sliders to check the values. If you have a mouse, you can check the values by hovering over the graph. If you are using only a keyboard, you can move the slider to move the horizontal axis of the graph to check the value of the graph as a pitch according to the location.</p>"""
    )
    show(column(message1, message2, row(plot, column(sliders))))

    if script_name != "":
        from bs4 import BeautifulSoup

        HTML = """
        <button id="unmuteButton">Push here to unmute graph</button>
        <script>
          document.getElementById('unmuteButton').addEventListener('click', function() {
            audioContext = new (window.AudioContext || window.webkitAudioContext)();
            audioGain = audioContext.createGain();
            panNode = audioContext.createStereoPanner();
            osc = audioContext.createOscillator();
            osc.connect(panNode);
            panNode.connect(audioGain);
            audioGain.connect(audioContext.destination);
            osc.start(audioContext.currentTime);
            audioGain.gain.setValueAtTime(0, audioContext.currentTime);
            oscTarget = 0;
          })
        </script>
        """

        html_filename = script_name.replace(".py", ".html")
        soup = BeautifulSoup(open(html_filename), 'html.parser')
        soup.body.insert(0, BeautifulSoup(HTML, "html.parser"))  # after body

        with open(html_filename, "w") as file:
            file.write(str(soup))
示例#25
0
def multi_channel_tile_slider(dataset: TileFeaturesDataset):
    """
    View interactively with bokeh the 3 image tiles
    """
    n = 100
    a_img_paths, b_img_paths, c_img_paths = _save_tile_images_to_local_path(
        dataset, n)

    # the plotting code
    plots = []
    sources = []
    pathes = [a_img_paths, b_img_paths, c_img_paths]
    plot_num = 3

    for i in range(plot_num):
        p = figure(height=300, width=300)
        img_paths = pathes[i]
        # print(img_paths)
        source = ColumnDataSource(data=dict(url=[img_paths[0]] * n,
                                            url_orig=img_paths,
                                            x=[1] * n,
                                            y=[1] * n,
                                            w=[1] * n,
                                            h=[1] * n))
        image = ImageURL(url="url",
                         x="x",
                         y="y",
                         w="w",
                         h="h",
                         anchor="bottom_left")
        p.add_glyph(source, glyph=image)
        _disable_all_for_pictures(p)

        plots.append(p)
        sources.append(source)

    update_source_str = """

        var data = source{i}.data;    
        url = data['url']
        url_orig = data['url_orig']
        for (i = 0; i < url_orig.length; i++) {
            url[i] = url_orig[f-1]
        }
        source{i}.change.emit();

    """
    # the callback
    callback = CustomJS(args=dict(source0=sources[0],
                                  source1=sources[1],
                                  source2=sources[2]),
                        code=f"""
        var f = cb_obj.value;
        console.log(f)
        {"".join([update_source_str.replace('{i}', str(i)) for i in range(plot_num)])}
    """)
    slider = Slider(start=1, end=n, value=1, step=1, title="example number")
    slider.js_on_change('value', callback)

    column_layout = [slider]
    curr_row = []
    for i in range(len(plots)):
        if i != 0 and i % 3 == 0:
            print(curr_row)
            column_layout.append(row(*curr_row.copy()))
            curr_row = []
        else:
            curr_row.append(plots[i])

    if len(curr_row) != 0:
        column_layout.append(row(*curr_row.copy()))

    layout = column(*column_layout)

    show(layout)
示例#26
0
def mru_fit_iterative():

    # mostra o gráfico no notebook
    output_notebook()

    # definindo os dados
    X_dados = np.asarray([0.000, 0.905, 1.833, 2.770, 3.684])
    Y_dados = np.asarray([0.00, 15.00, 30.00, 45.00, 60.00])

    # dados a serem mudados iterativamente
    t = X_dados
    s = t

    r2 = coef_determinacao(Y_dados, s)
    dummy_texts = ['R2 = {}'.format(r2), '', '', '', '']
    dummy_x = [0.1 for _ in X_dados]
    dummy_y = [35 for _ in X_dados]

    # cores a serem usadas no plot
    COLOR_DADOS = "#0095DD"
    COLOR_AJUSTE = "#E34A33"

    # cria display dinâmico de valores
    TOOLTIPS_POSICAO = [("Posição [cm]", "@y{0.00}"), ("Tempo [s]", "@x")]

    # cria a fonte dinâmica para representar o ajuste
    source_posicao = ColumnDataSource(data=dict(
        x=t, y=s, dados=Y_dados, text=dummy_texts, posx=dummy_x, posy=dummy_y))

    # cria a figura onde os dados e o ajuste serão mostrados
    plot_posicao = figure(plot_width=400,
                          plot_height=400,
                          tooltips=TOOLTIPS_POSICAO,
                          title='Ajuste da reta S(t) = a + bt.')

    # plotando os dados
    plot_posicao.line(X_dados,
                      Y_dados,
                      color=COLOR_DADOS,
                      legend='Dados',
                      line_width=3)

    plot_posicao.scatter(X_dados,
                         Y_dados,
                         color=COLOR_DADOS,
                         legend='Dados',
                         fill_color='#FFFFFF',
                         size=18)

    # plotando a reta de ajuste (dinâmica)
    plot_posicao.line('x',
                      'y',
                      source=source_posicao,
                      color=COLOR_AJUSTE,
                      legend='Ajuste',
                      line_width=3,
                      line_dash="5 5")

    plot_posicao.scatter('x',
                         'y',
                         source=source_posicao,
                         color=COLOR_AJUSTE,
                         legend='Ajuste',
                         marker='x',
                         size=16)

    # definindo os labels do axis
    plot_posicao.xaxis[0].axis_label = 'Tempo, t [s]'
    plot_posicao.yaxis[0].axis_label = 'Posição, S(t) [cm]'

    # mudando as fontes dos labels
    FONT_SIZE = '16px'

    plot_posicao.xaxis.axis_label_text_font_size = FONT_SIZE
    plot_posicao.xaxis.major_label_text_font_size = FONT_SIZE

    plot_posicao.yaxis.axis_label_text_font_size = FONT_SIZE
    plot_posicao.yaxis.major_label_text_font_size = FONT_SIZE

    plot_posicao.xaxis.axis_label_text_font_style = 'normal'
    plot_posicao.yaxis.axis_label_text_font_style = 'normal'

    # remove a barra lateral com o símbolo do bokeh
    plot_posicao.toolbar.logo = None
    plot_posicao.toolbar_location = None

    # mudando a legenda de lugar
    plot_posicao.legend.location = 'top_left'

    # criando o label

    citation = LabelSet(x=1.5,
                        y=55,
                        text='text',
                        source=source_posicao,
                        render_mode='css',
                        border_line_color='black',
                        background_fill_color='white')

    plot_posicao.add_layout(citation)

    # criando os sliders
    slider_posicao_inicial = Slider(
        start=-10,
        end=10,
        value=0,
        step=-0.01,
        bar_color=COLOR_AJUSTE,
        title="Coeficiente linear, a"
    )  #orientation='vertical', direction='rtl')
    slider_velocidade = Slider(start=-20,
                               end=20,
                               value=5,
                               step=-0.05,
                               bar_color=COLOR_AJUSTE,
                               title="Coeficiente angular, b"
                               )  #orientation='vertical', direction='rtl')

    # cirando o callback para mudar a reta de ajuste dinamicamente
    callback_ajuste = CustomJS(args=dict(
        source=source_posicao,
        velocidade=slider_velocidade,
        posicao_inicial=slider_posicao_inicial),
                               code="""
        const data  = source.data;
        const v     = velocidade.value;
        const s0    = posicao_inicial.value;
        const t     = data['x'];
        const s     = data['y'];
        const yDado = data['dados'];
        const text  = data['text'];

        
        for (let i = 0; i < t.length; i++) {
        s[i] = s0 + v*t[i];
        }

        let mean_value = (arr) => {
            let sum = 0;
            for (let i = 0; i < arr.length; i++) {
                sum += arr[i];
            }

            let mean = sum/arr.length;

            return mean;
        }

        let r2 = (y_dado, y_ajuste) => {
            let s_res = 0;
            let s_tot = 0;

            let mean_dado = mean_value(y_dado);

            for (let i = 0; i < y_dado.length; i++) {
                s_res += (y_dado[i] - y_ajuste[i])**2;
                s_tot += (y_dado[i] - mean_dado)**2;
            }

            let score = Math.round( (1 - s_res/s_tot)*1000 ) / 1000;

            if (score >= 0) {
                return score;
            } else {
                return 'nan';
            }

        }

        text[0] = `R2 = ${r2(yDado, s).toString()}`;

        source.change.emit();
        """)

    # criando os sliders
    slider_posicao_inicial.js_on_change('value', callback_ajuste)
    slider_velocidade.js_on_change('value', callback_ajuste)
    # citation.js_on_change('value', callback_ajuste)

    # criando o layout de como ficará disposto o gráfico e os sliders
    layout = row([
        plot_posicao,
        column([
            slider_posicao_inicial,
            slider_velocidade,
        ],
               sizing_mode='scale_width'),
    ],
                 sizing_mode='scale_width')

    # mostrando o plot
    show(layout)
示例#27
0
       return v | 0
    }
    const color = source.data['color']
    const text_color = source.data['text_color']
    const R = toInt(red.value)
    const G = toInt(green.value)
    const B = toInt(blue.value)
    color[0] = rgbToHex(R, G, B)
    text_color[0] = '#ffffff'
    if ((R > 127) || (G > 127) || (B > 127)) {
        text_color[0] = '#000000'
    }
    source.change.emit()
""")

red_slider.js_on_change('value', callback)
blue_slider.js_on_change('value', callback)
green_slider.js_on_change('value', callback)

# plot 2: create a color spectrum with a hover-over tool to inspect hex codes

brightness = 0.8  # change to have brighter/darker colors
crx = list(range(1, 1001))  # the resolution is 1000 colors
cry = [5 for i in range(len(crx))]
crcolor, crRGBs = generate_color_range(1000, brightness)  # produce spectrum

# make data source object to allow information to be displayed by hover tool
crsource = ColumnDataSource(
    data=dict(x=crx, y=cry, crcolor=crcolor, RGBs=crRGBs))

# create second plot
示例#28
0
# source = ColumnDataSource(data=data)
kguess = (np.max(yobs)-np.min(yobs))/(np.max(xobs)-np.min(xobs)) *5.
k_slider = Slider(start=-1, end=0, value=1, step=0.001, title="k")
b_slider = Slider(start=-1000, end=1000, value=1, step=0.001, title="b")


callback = CustomJS(args=dict(lineColumn=lineColumn, kval=k_slider, bval=b_slider),
                    code="""
    const data = lineColumn.data ;
    const k = kval.value ;
    const b = bval.value ;
    const x = data['xline']
    const y = data['yline']
    for (var i = 0; i < x.length; i++) {
        y[i] = b + k*x[i] ;
    }
    lineColumn.change.emit();
""")


k_slider.js_on_change('value', callback)
b_slider.js_on_change('value', callback)

layout = row(
    ax1, 
    column(k_slider, b_slider),
)

output_file("2.html", title="der")

show(layout)
        }
        source.change.emit();
    """)


callback_ion = CustomJS(args=dict(source=source), code="""
        var data = source.data;
        var f = cb_obj.range
        var x = data['x']
        var y = data['y']
        var pow = (Math.log(y[100])/Math.log(x[100]))
        console.log(pow)
        var delta = (f[1] - f[0])/x.length
        for (var i = 0; i < x.length; i++) {
            x[i] = delta*i + f[0]
            y[i] = Math.pow(x[i], pow)
        }
        source.change.emit();
    """)


slider = Slider(start=0, end=5, step=0.1, value=1, title="Bokeh Slider - Power")
slider.js_on_change('value', callback_single)

ion_range_slider = IonRangeSlider(start=0.01, end=0.99, step=0.01, range=(min(x), max(x)),
    title='Ion Range Slider - Range')
ion_range_slider.js_on_change('range', callback_ion)

layout = column(plot, slider, ion_range_slider)
show(layout)
示例#30
0
文件: plot.py 项目: alucab/skyportal
def photometry_plot(obj_id, user, width=600, height=300, device="browser"):
    """Create object photometry scatter plot.

    Parameters
    ----------
    obj_id : str
        ID of Obj to be plotted.

    Returns
    -------
    dict
        Returns Bokeh JSON embedding for the desired plot.
    """

    data = pd.read_sql(
        DBSession().query(
            Photometry,
            Telescope.nickname.label("telescope"),
            Instrument.name.label("instrument"),
        ).join(Instrument, Instrument.id == Photometry.instrument_id).join(
            Telescope, Telescope.id == Instrument.telescope_id).filter(
                Photometry.obj_id == obj_id).filter(
                    Photometry.groups.any(
                        Group.id.in_([g.id for g in user.accessible_groups
                                      ]))).statement,
        DBSession().bind,
    )

    if data.empty:
        return None, None, None

    # get spectra to annotate on phot plots
    spectra = (Spectrum.query_records_accessible_by(user).filter(
        Spectrum.obj_id == obj_id).all())

    data['color'] = [get_color(f) for f in data['filter']]

    labels = []
    for i, datarow in data.iterrows():
        label = f'{datarow["instrument"]}/{datarow["filter"]}'
        if datarow['origin'] is not None:
            label += f'/{datarow["origin"]}'
        labels.append(label)

    data['label'] = labels
    data['zp'] = PHOT_ZP
    data['magsys'] = 'ab'
    data['alpha'] = 1.0
    data['lim_mag'] = (
        -2.5 * np.log10(data['fluxerr'] * PHOT_DETECTION_THRESHOLD) +
        data['zp'])

    # Passing a dictionary to a bokeh datasource causes the frontend to die,
    # deleting the dictionary column fixes that
    del data['original_user_data']

    # keep track of things that are only upper limits
    data['hasflux'] = ~data['flux'].isna()

    # calculate the magnitudes - a photometry point is considered "significant"
    # or "detected" (and thus can be represented by a magnitude) if its snr
    # is above PHOT_DETECTION_THRESHOLD
    obsind = data['hasflux'] & (data['flux'].fillna(0.0) / data['fluxerr'] >=
                                PHOT_DETECTION_THRESHOLD)
    data.loc[~obsind, 'mag'] = None
    data.loc[obsind, 'mag'] = -2.5 * np.log10(data[obsind]['flux']) + PHOT_ZP

    # calculate the magnitude errors using standard error propagation formulae
    # https://en.wikipedia.org/wiki/Propagation_of_uncertainty#Example_formulae
    data.loc[~obsind, 'magerr'] = None
    coeff = 2.5 / np.log(10)
    magerrs = np.abs(coeff * data[obsind]['fluxerr'] / data[obsind]['flux'])
    data.loc[obsind, 'magerr'] = magerrs
    data['obs'] = obsind
    data['stacked'] = False

    split = data.groupby('label', sort=False)

    finite = np.isfinite(data['flux'])
    fdata = data[finite]
    lower = np.min(fdata['flux']) * 0.95
    upper = np.max(fdata['flux']) * 1.05

    active_drag = None if "mobile" in device or "tablet" in device else "box_zoom"
    tools = ('box_zoom,pan,reset' if "mobile" in device or "tablet" in device
             else "box_zoom,wheel_zoom,pan,reset,save")

    plot = figure(
        aspect_ratio=2.0 if device == "mobile_landscape" else 1.5,
        sizing_mode='scale_both',
        active_drag=active_drag,
        tools=tools,
        toolbar_location='above',
        toolbar_sticky=True,
        y_range=(lower, upper),
        min_border_right=16,
    )
    imhover = HoverTool(tooltips=tooltip_format)
    imhover.renderers = []
    plot.add_tools(imhover)

    model_dict = {}

    for i, (label, sdf) in enumerate(split):

        # for the flux plot, we only show things that have a flux value
        df = sdf[sdf['hasflux']]

        key = f'obs{i}'
        model_dict[key] = plot.scatter(
            x='mjd',
            y='flux',
            color='color',
            marker='circle',
            fill_color='color',
            alpha='alpha',
            source=ColumnDataSource(df),
        )

        imhover.renderers.append(model_dict[key])

        key = f'bin{i}'
        model_dict[key] = plot.scatter(
            x='mjd',
            y='flux',
            color='color',
            marker='circle',
            fill_color='color',
            source=ColumnDataSource(data=dict(
                mjd=[],
                flux=[],
                fluxerr=[],
                filter=[],
                color=[],
                lim_mag=[],
                mag=[],
                magerr=[],
                stacked=[],
                instrument=[],
            )),
        )

        imhover.renderers.append(model_dict[key])

        key = 'obserr' + str(i)
        y_err_x = []
        y_err_y = []

        for d, ro in df.iterrows():
            px = ro['mjd']
            py = ro['flux']
            err = ro['fluxerr']

            y_err_x.append((px, px))
            y_err_y.append((py - err, py + err))

        model_dict[key] = plot.multi_line(
            xs='xs',
            ys='ys',
            color='color',
            alpha='alpha',
            source=ColumnDataSource(data=dict(xs=y_err_x,
                                              ys=y_err_y,
                                              color=df['color'],
                                              alpha=[1.0] * len(df))),
        )

        key = f'binerr{i}'
        model_dict[key] = plot.multi_line(
            xs='xs',
            ys='ys',
            color='color',
            source=ColumnDataSource(data=dict(xs=[], ys=[], color=[])),
        )

    plot.xaxis.axis_label = 'MJD'
    if device == "mobile_portrait":
        plot.xaxis.ticker.desired_num_ticks = 5
    plot.yaxis.axis_label = 'Flux (μJy)'
    plot.toolbar.logo = None

    colors_labels = data[['color', 'label']].drop_duplicates()

    toggle = CheckboxWithLegendGroup(
        labels=colors_labels.label.tolist(),
        active=list(range(len(colors_labels))),
        colors=colors_labels.color.tolist(),
        width=width // 5,
        inline=True if "tablet" in device else False,
    )

    # TODO replace `eval` with Namespaces
    # https://github.com/bokeh/bokeh/pull/6340
    toggle.js_on_click(
        CustomJS(
            args={
                'toggle': toggle,
                **model_dict
            },
            code=open(
                os.path.join(os.path.dirname(__file__), '../static/js/plotjs',
                             'togglef.js')).read(),
        ))

    slider = Slider(
        start=0.0,
        end=15.0,
        value=0.0,
        step=1.0,
        title='Binsize (days)',
        max_width=350,
        margin=(4, 10, 0, 10),
    )

    callback = CustomJS(
        args={
            'slider': slider,
            'toggle': toggle,
            **model_dict
        },
        code=open(
            os.path.join(os.path.dirname(__file__),
                         '../static/js/plotjs', 'stackf.js')).read().replace(
                             'default_zp', str(PHOT_ZP)).replace(
                                 'detect_thresh',
                                 str(PHOT_DETECTION_THRESHOLD)),
    )

    slider.js_on_change('value', callback)

    # Mark the first and last detections
    detection_dates = data[data['hasflux']]['mjd']
    if len(detection_dates) > 0:
        first = round(detection_dates.min(), 6)
        last = round(detection_dates.max(), 6)
        first_color = "#34b4eb"
        last_color = "#8992f5"
        midpoint = (upper + lower) / 2
        line_top = 5 * upper - 4 * midpoint
        line_bottom = 5 * lower - 4 * midpoint
        y = np.linspace(line_bottom, line_top, num=5000)
        first_r = plot.line(
            x=np.full(5000, first),
            y=y,
            line_alpha=0.5,
            line_color=first_color,
            line_width=2,
        )
        plot.add_tools(
            HoverTool(
                tooltips=[("First detection", f'{first}')],
                renderers=[first_r],
            ))
        last_r = plot.line(
            x=np.full(5000, last),
            y=y,
            line_alpha=0.5,
            line_color=last_color,
            line_width=2,
        )
        plot.add_tools(
            HoverTool(
                tooltips=[("Last detection", f'{last}')],
                renderers=[last_r],
            ))

    # Mark when spectra were taken
    annotate_spec(plot, spectra, lower, upper)

    plot_layout = (column(plot, toggle) if "mobile" in device
                   or "tablet" in device else row(plot, toggle))
    layout = column(slider,
                    plot_layout,
                    sizing_mode='scale_width',
                    width=width)

    p1 = Panel(child=layout, title='Flux')

    # now make the mag light curve
    ymax = (np.nanmax((
        np.nanmax(data.loc[obsind, 'mag']) if any(obsind) else np.nan,
        np.nanmax(data.loc[~obsind, 'lim_mag']) if any(~obsind) else np.nan,
    )) + 0.1)
    ymin = (np.nanmin((
        np.nanmin(data.loc[obsind, 'mag']) if any(obsind) else np.nan,
        np.nanmin(data.loc[~obsind, 'lim_mag']) if any(~obsind) else np.nan,
    )) - 0.1)

    xmin = data['mjd'].min() - 2
    xmax = data['mjd'].max() + 2

    plot = figure(
        aspect_ratio=2.0 if device == "mobile_landscape" else 1.5,
        sizing_mode='scale_both',
        width=width,
        active_drag=active_drag,
        tools=tools,
        y_range=(ymax, ymin),
        x_range=(xmin, xmax),
        toolbar_location='above',
        toolbar_sticky=True,
        x_axis_location='above',
    )

    # Mark the first and last detections again
    detection_dates = data[obsind]['mjd']
    if len(detection_dates) > 0:
        first = round(detection_dates.min(), 6)
        last = round(detection_dates.max(), 6)
        midpoint = (ymax + ymin) / 2
        line_top = 5 * ymax - 4 * midpoint
        line_bottom = 5 * ymin - 4 * midpoint
        y = np.linspace(line_bottom, line_top, num=5000)
        first_r = plot.line(
            x=np.full(5000, first),
            y=y,
            line_alpha=0.5,
            line_color=first_color,
            line_width=2,
        )
        plot.add_tools(
            HoverTool(
                tooltips=[("First detection", f'{first}')],
                renderers=[first_r],
            ))
        last_r = plot.line(
            x=np.full(5000, last),
            y=y,
            line_alpha=0.5,
            line_color=last_color,
            line_width=2,
        )
        plot.add_tools(
            HoverTool(
                tooltips=[("Last detection", f'{last}')],
                renderers=[last_r],
                point_policy='follow_mouse',
            ))

    # Mark when spectra were taken
    annotate_spec(plot, spectra, ymax, ymin)

    imhover = HoverTool(tooltips=tooltip_format)
    imhover.renderers = []
    plot.add_tools(imhover)

    model_dict = {}

    for i, (label, df) in enumerate(split):

        key = f'obs{i}'
        model_dict[key] = plot.scatter(
            x='mjd',
            y='mag',
            color='color',
            marker='circle',
            fill_color='color',
            alpha='alpha',
            source=ColumnDataSource(df[df['obs']]),
        )

        imhover.renderers.append(model_dict[key])

        unobs_source = df[~df['obs']].copy()
        unobs_source.loc[:, 'alpha'] = 0.8

        key = f'unobs{i}'
        model_dict[key] = plot.scatter(
            x='mjd',
            y='lim_mag',
            color='color',
            marker='inverted_triangle',
            fill_color='white',
            line_color='color',
            alpha='alpha',
            source=ColumnDataSource(unobs_source),
        )

        imhover.renderers.append(model_dict[key])

        key = f'bin{i}'
        model_dict[key] = plot.scatter(
            x='mjd',
            y='mag',
            color='color',
            marker='circle',
            fill_color='color',
            source=ColumnDataSource(data=dict(
                mjd=[],
                flux=[],
                fluxerr=[],
                filter=[],
                color=[],
                lim_mag=[],
                mag=[],
                magerr=[],
                instrument=[],
                stacked=[],
            )),
        )

        imhover.renderers.append(model_dict[key])

        key = 'obserr' + str(i)
        y_err_x = []
        y_err_y = []

        for d, ro in df[df['obs']].iterrows():
            px = ro['mjd']
            py = ro['mag']
            err = ro['magerr']

            y_err_x.append((px, px))
            y_err_y.append((py - err, py + err))

        model_dict[key] = plot.multi_line(
            xs='xs',
            ys='ys',
            color='color',
            alpha='alpha',
            source=ColumnDataSource(data=dict(
                xs=y_err_x,
                ys=y_err_y,
                color=df[df['obs']]['color'],
                alpha=[1.0] * len(df[df['obs']]),
            )),
        )

        key = f'binerr{i}'
        model_dict[key] = plot.multi_line(
            xs='xs',
            ys='ys',
            color='color',
            source=ColumnDataSource(data=dict(xs=[], ys=[], color=[])),
        )

        key = f'unobsbin{i}'
        model_dict[key] = plot.scatter(
            x='mjd',
            y='lim_mag',
            color='color',
            marker='inverted_triangle',
            fill_color='white',
            line_color='color',
            alpha=0.8,
            source=ColumnDataSource(data=dict(
                mjd=[],
                flux=[],
                fluxerr=[],
                filter=[],
                color=[],
                lim_mag=[],
                mag=[],
                magerr=[],
                instrument=[],
                stacked=[],
            )),
        )
        imhover.renderers.append(model_dict[key])

        key = f'all{i}'
        model_dict[key] = ColumnDataSource(df)

        key = f'bold{i}'
        model_dict[key] = ColumnDataSource(df[[
            'mjd',
            'flux',
            'fluxerr',
            'mag',
            'magerr',
            'filter',
            'zp',
            'magsys',
            'lim_mag',
            'stacked',
        ]])

    plot.xaxis.axis_label = 'MJD'
    plot.yaxis.axis_label = 'AB mag'
    plot.toolbar.logo = None

    obj = DBSession().query(Obj).get(obj_id)
    if obj.dm is not None:
        plot.extra_y_ranges = {
            "Absolute Mag": Range1d(start=ymax - obj.dm, end=ymin - obj.dm)
        }
        plot.add_layout(
            LinearAxis(y_range_name="Absolute Mag", axis_label="m - DM"),
            'right')

    now = Time.now().mjd
    plot.extra_x_ranges = {
        "Days Ago": Range1d(start=now - xmin, end=now - xmax)
    }
    plot.add_layout(LinearAxis(x_range_name="Days Ago", axis_label="Days Ago"),
                    'below')

    colors_labels = data[['color', 'label']].drop_duplicates()

    toggle = CheckboxWithLegendGroup(
        labels=colors_labels.label.tolist(),
        active=list(range(len(colors_labels))),
        colors=colors_labels.color.tolist(),
        width=width // 5,
        inline=True if "tablet" in device else False,
    )

    # TODO replace `eval` with Namespaces
    # https://github.com/bokeh/bokeh/pull/6340
    toggle.js_on_click(
        CustomJS(
            args={
                'toggle': toggle,
                **model_dict
            },
            code=open(
                os.path.join(os.path.dirname(__file__), '../static/js/plotjs',
                             'togglem.js')).read(),
        ))

    slider = Slider(
        start=0.0,
        end=15.0,
        value=0.0,
        step=1.0,
        title='Binsize (days)',
        max_width=350,
        margin=(4, 10, 0, 10),
    )

    button = Button(label="Export Bold Light Curve to CSV")
    button.js_on_click(
        CustomJS(
            args={
                'slider': slider,
                'toggle': toggle,
                **model_dict
            },
            code=open(
                os.path.join(os.path.dirname(__file__), '../static/js/plotjs',
                             "download.js")).read().replace('objname',
                                                            obj_id).replace(
                                                                'default_zp',
                                                                str(PHOT_ZP)),
        ))

    # Don't need to expose CSV download on mobile
    top_layout = (slider if "mobile" in device or "tablet" in device else row(
        slider, button))

    callback = CustomJS(
        args={
            'slider': slider,
            'toggle': toggle,
            **model_dict
        },
        code=open(
            os.path.join(os.path.dirname(__file__),
                         '../static/js/plotjs', 'stackm.js')).read().replace(
                             'default_zp', str(PHOT_ZP)).replace(
                                 'detect_thresh',
                                 str(PHOT_DETECTION_THRESHOLD)),
    )
    slider.js_on_change('value', callback)
    plot_layout = (column(plot, toggle) if "mobile" in device
                   or "tablet" in device else row(plot, toggle))
    layout = column(top_layout,
                    plot_layout,
                    sizing_mode='scale_width',
                    width=width)

    p2 = Panel(child=layout, title='Mag')

    # now make period plot

    # get periods from annotations
    annotation_list = obj.get_annotations_readable_by(user)
    period_labels = []
    period_list = []
    for an in annotation_list:
        if 'period' in an.data:
            period_list.append(an.data['period'])
            period_labels.append(an.origin + ": %.9f" % an.data['period'])

    if len(period_list) > 0:
        period = period_list[0]
    else:
        period = None

    # don't generate if no period annotated
    if period is not None:

        # bokeh figure for period plotting
        period_plot = figure(
            aspect_ratio=1.5,
            sizing_mode='scale_both',
            active_drag='box_zoom',
            tools='box_zoom,wheel_zoom,pan,reset,save',
            y_range=(ymax, ymin),
            x_range=(-0.1, 1.1),  # initially one phase
            toolbar_location='above',
            toolbar_sticky=False,
            x_axis_location='below',
        )

        # axis labels
        period_plot.xaxis.axis_label = 'phase'
        period_plot.yaxis.axis_label = 'mag'
        period_plot.toolbar.logo = None

        # do we have a distance modulus (dm)?
        obj = DBSession().query(Obj).get(obj_id)
        if obj.dm is not None:
            period_plot.extra_y_ranges = {
                "Absolute Mag": Range1d(start=ymax - obj.dm, end=ymin - obj.dm)
            }
            period_plot.add_layout(
                LinearAxis(y_range_name="Absolute Mag", axis_label="m - DM"),
                'right')

        # initiate hover tool
        period_imhover = HoverTool(tooltips=tooltip_format)
        period_imhover.renderers = []
        period_plot.add_tools(period_imhover)

        # initiate period radio buttons
        period_selection = RadioGroup(labels=period_labels, active=0)

        phase_selection = RadioGroup(labels=["One phase", "Two phases"],
                                     active=0)

        # store all the plot data
        period_model_dict = {}

        # iterate over each filter
        for i, (label, df) in enumerate(split):

            # fold x-axis on period in days
            df['mjd_folda'] = (df['mjd'] % period) / period
            df['mjd_foldb'] = df['mjd_folda'] + 1.0

            # phase plotting
            for ph in ['a', 'b']:
                key = 'fold' + ph + f'{i}'
                period_model_dict[key] = period_plot.scatter(
                    x='mjd_fold' + ph,
                    y='mag',
                    color='color',
                    marker='circle',
                    fill_color='color',
                    alpha='alpha',
                    visible=('a' in ph),
                    source=ColumnDataSource(
                        df[df['obs']]),  # only visible data
                )
                # add to hover tool
                period_imhover.renderers.append(period_model_dict[key])

                # errorbars for phases
                key = 'fold' + ph + f'err{i}'
                y_err_x = []
                y_err_y = []

                # get each visible error value
                for d, ro in df[df['obs']].iterrows():
                    px = ro['mjd_fold' + ph]
                    py = ro['mag']
                    err = ro['magerr']
                    # set up error tuples
                    y_err_x.append((px, px))
                    y_err_y.append((py - err, py + err))
                # plot phase errors
                period_model_dict[key] = period_plot.multi_line(
                    xs='xs',
                    ys='ys',
                    color='color',
                    alpha='alpha',
                    visible=('a' in ph),
                    source=ColumnDataSource(data=dict(
                        xs=y_err_x,
                        ys=y_err_y,
                        color=df[df['obs']]['color'],
                        alpha=[1.0] * len(df[df['obs']]),
                    )),
                )

        # toggle for folded photometry
        period_toggle = CheckboxWithLegendGroup(
            labels=colors_labels.label.tolist(),
            active=list(range(len(colors_labels))),
            colors=colors_labels.color.tolist(),
            width=width // 5,
        )
        # use javascript to perform toggling on click
        # TODO replace `eval` with Namespaces
        # https://github.com/bokeh/bokeh/pull/6340
        period_toggle.js_on_click(
            CustomJS(
                args={
                    'toggle': period_toggle,
                    'numphases': phase_selection,
                    'p': period_plot,
                    **period_model_dict,
                },
                code=open(
                    os.path.join(os.path.dirname(__file__),
                                 '../static/js/plotjs', 'togglep.js')).read(),
            ))

        # set up period adjustment text box
        period_title = Div(text="Period (days): ")
        period_textinput = TextInput(
            value=str(period if period is not None else 0.0))
        period_textinput.js_on_change(
            'value',
            CustomJS(
                args={
                    'textinput': period_textinput,
                    'toggle': period_toggle,
                    'numphases': phase_selection,
                    'p': period_plot,
                    **period_model_dict,
                },
                code=open(
                    os.path.join(os.path.dirname(__file__),
                                 '../static/js/plotjs',
                                 'foldphase.js')).read(),
            ),
        )
        # a way to modify the period
        period_double_button = Button(label="*2")
        period_double_button.js_on_click(
            CustomJS(
                args={'textinput': period_textinput},
                code="""
                const period = parseFloat(textinput.value);
                textinput.value = parseFloat(2.*period).toFixed(9);
                """,
            ))
        period_halve_button = Button(label="/2")
        period_halve_button.js_on_click(
            CustomJS(
                args={'textinput': period_textinput},
                code="""
                        const period = parseFloat(textinput.value);
                        textinput.value = parseFloat(period/2.).toFixed(9);
                        """,
            ))
        # a way to select the period
        period_selection.js_on_click(
            CustomJS(
                args={
                    'textinput': period_textinput,
                    'periods': period_list
                },
                code="""
                textinput.value = parseFloat(periods[this.active]).toFixed(9);
                """,
            ))
        phase_selection.js_on_click(
            CustomJS(
                args={
                    'textinput': period_textinput,
                    'toggle': period_toggle,
                    'numphases': phase_selection,
                    'p': period_plot,
                    **period_model_dict,
                },
                code=open(
                    os.path.join(os.path.dirname(__file__),
                                 '../static/js/plotjs',
                                 'foldphase.js')).read(),
            ))

        # layout

        period_column = column(
            period_toggle,
            period_title,
            period_textinput,
            period_selection,
            row(period_double_button, period_halve_button, width=180),
            phase_selection,
            width=180,
        )

        period_layout = column(
            row(period_plot, period_column),
            sizing_mode='scale_width',
            width=width,
        )

        # Period panel
        p3 = Panel(child=period_layout, title='Period')
        # tabs for mag, flux, period
        tabs = Tabs(tabs=[p2, p1, p3],
                    width=width,
                    height=height,
                    sizing_mode='fixed')
    else:
        # tabs for mag, flux
        tabs = Tabs(tabs=[p2, p1],
                    width=width,
                    height=height,
                    sizing_mode='fixed')
    return bokeh_embed.json_item(tabs)
示例#31
0
output_file("js_on_change.html")

x = [x * 0.005 for x in range(0, 200)]
y = x

source = ColumnDataSource(data=dict(x=x, y=y))

plot = Figure(plot_width=400, plot_height=400)
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6, color='red')

callback = CustomJS(args=dict(source=source),
                    code="""
    var data = source.data;
    var f = cb_obj.value
    var x = data['x']
    var y = data['y']
    for (var i = 0; i < x.length; i++) {
        y[i] = Math.pow(x[i], f)
    }
    source.change.emit();
""")

slider = Slider(start=0.1, end=4, value=1, step=.1, title="power")
slider.js_on_change('value', callback)

lay = layout([slider], [plot])

content = interact_plot_grid(lay)

description = 'Interact Plot'
示例#32
0
def geoplot(gdf_in,
            fig=None,
            figsize=None,
            title="",
            xlabel="Longitude",
            ylabel="Latitude",
            color="blue",
            colormap=None,
            colormap_uselog=False,
            colormap_range=None,
            category=None,
            dropdown=None,
            slider=None,
            slider_range=None,
            slider_name="",
            show_colorbar=True,
            xrange=None,
            yrange=None,
            hovertool=True,
            hovertool_columns=[],
            simplify_shapes=None,
            tile_provider="CARTODBPOSITRON_RETINA",
            tile_provider_url=None,
            tile_attribution="",
            toolbar_location="right",
            show_figure=True,
            return_figure=True,
            return_html=False,
            legend=True,
            webgl=True,
            **kwargs):
    """Doc-String: TODO"""

    gdf = gdf_in.copy()

    # Check layertypes:
    layertypes = []
    if "Point" in str(gdf.geom_type.unique()):
        layertypes.append("Point")
    if "Line" in str(gdf.geom_type.unique()):
        layertypes.append("Line")
    if "Polygon" in str(gdf.geom_type.unique()):
        layertypes.append("Polygon")
    if len(layertypes) > 1:
        raise Exception(
            "Can only plot GeoDataFrames/Series with single type of geometry (either Point, Line or Polygon). Provided is a GeoDataFrame/Series with types: %s"
            % layertypes)

    # Get and check provided parameters for geoplot:
    figure_options = {
        "title": title,
        "x_axis_label": xlabel,
        "y_axis_label": ylabel,
        "plot_width": 600,
        "plot_height": 400,
        "toolbar_location": toolbar_location,
        "active_scroll": "wheel_zoom",
    }
    if not figsize is None:
        width, height = figsize
        figure_options["plot_width"] = width
        figure_options["plot_height"] = height
    if webgl:
        figure_options["output_backend"] = "webgl"

    if not fig is None:
        raise NotImplementedError("Parameter <figure> not yet implemented.")

    # Convert GeoDataFrame to Web Mercador Projection:
    gdf.to_crs({"init": "epsg:3857"}, inplace=True)

    # Simplify shapes if wanted:
    if isinstance(simplify_shapes, numbers.Number):
        if layertypes[0] in ["Line", "Polygon"]:
            gdf["geometry"] = gdf["geometry"].simplify(simplify_shapes)
    elif not simplify_shapes is None:
        raise ValueError(
            "<simplify_shapes> parameter only accepts numbers or None.")

    # Check for category, dropdown or slider (choropleth map column):
    category_options = 0
    if not category is None:
        category_options += 1
        category_columns = [category]
    if not dropdown is None:
        category_options += 1
        category_columns = dropdown
    if not slider is None:
        category_options += 1
        category_columns = slider
    if category_options > 1:
        raise ValueError(
            "Only one of <category>, <dropdown> or <slider> parameters is allowed to be used at once."
        )

    # Check for category (single choropleth plot):
    if category is None:
        pass
    elif isinstance(category, (list, tuple)):
        raise ValueError(
            "For <category>, please provide an existing single column of the GeoDataFrame."
        )
    elif category in gdf.columns:
        pass
    else:
        raise ValueError(
            "Could not find column '%s' in GeoDataFrame. For <category>, please provide an existing single column of the GeoDataFrame."
            % category)

    # Check for dropdown (multiple choropleth plots via dropdown selection):
    if dropdown is None:
        pass
    elif not isinstance(dropdown, (list, tuple)):
        raise ValueError(
            "For <dropdown>, please provide a list/tuple of existing columns of the GeoDataFrame."
        )
    else:
        for col in dropdown:
            if col not in gdf.columns:
                raise ValueError(
                    "Could not find column '%s' for <dropdown> in GeoDataFrame. "
                    % col)

    # Check for slider (multiple choropleth plots via slider selection):
    if slider is None:
        pass
    elif not isinstance(slider, (list, tuple)):
        raise ValueError(
            "For <slider>, please provide a list/tuple of existing columns of the GeoDataFrame."
        )
    else:
        for col in slider:
            if col not in gdf.columns:
                raise ValueError(
                    "Could not find column '%s' for <slider> in GeoDataFrame. "
                    % col)

        if not slider_range is None:
            if not isinstance(slider_range, Iterable):
                raise ValueError(
                    "<slider_range> has to be a type that is iterable like list, tuple, range, ..."
                )
            else:
                slider_range = list(slider_range)
                if len(slider_range) != len(slider):
                    raise ValueError(
                        "The number of elements in <slider_range> has to be the same as in <slider>."
                    )
                steps = []
                for i in range(len(slider_range) - 1):
                    steps.append(slider_range[i + 1] - slider_range[i])

                if len(set(steps)) > 1:
                    raise ValueError(
                        "<slider_range> has to have equal step size between each elements (like a range-object)."
                    )
                else:
                    slider_step = steps[0]
                    slider_start = slider_range[0]
                    slider_end = slider_range[-1]

    # Check colormap if either <category>, <dropdown> or <slider> is choosen:
    if category_options == 1:
        if colormap is None:
            colormap = blue_colormap
        elif isinstance(colormap, (tuple, list)):
            if len(colormap) > 1:
                pass
            else:
                raise ValueError(
                    "<colormap> only accepts a list/tuple of at least two colors or the name of one of the following predefined colormaps (see also https://bokeh.pydata.org/en/latest/docs/reference/palettes.html ): %s"
                    % (list(all_palettes.keys())))
        elif isinstance(colormap, str):
            if colormap in all_palettes:
                colormap = all_palettes[colormap]
                colormap = colormap[max(colormap.keys())]
            else:
                raise ValueError(
                    "Could not find <colormap> with name %s. The following predefined colormaps are supported (see also https://bokeh.pydata.org/en/latest/docs/reference/palettes.html ): %s"
                    % (colormap, list(all_palettes.keys())))
        else:
            raise ValueError(
                "<colormap> only accepts a list/tuple of at least two colors or the name of one of the following predefined colormaps (see also https://bokeh.pydata.org/en/latest/docs/reference/palettes.html ): %s"
                % (list(all_palettes.keys())))
    else:
        if isinstance(color, str):
            colormap = [color]
        else:
            raise ValueError(
                "<color> has to be a string specifying the fill_color of the map glyph."
            )

    # Create Figure to draw:
    p = figure(x_axis_type="mercator",
               y_axis_type="mercator",
               **figure_options)

    # Get ridd of zoom on axes:
    for t in p.tools:
        if type(t) == WheelZoomTool:
            t.zoom_on_axis = False

    # Add Tile Source as Background:
    p = _add_backgroundtile(p, tile_provider, tile_provider_url,
                            tile_attribution)

    # Hide legend if wanted:
    if not legend:
        p.legend.visible = False
    elif isinstance(legend, str):
        pass
    else:
        legend = "GeoLayer"

    # Define colormapper:
    if len(colormap) == 1:
        kwargs["fill_color"] = colormap[0]

    elif not category is None:
        # Check if category column is numerical:
        if not issubclass(gdf[category].dtype.type, np.number):
            raise NotImplementedError(
                "<category> plot only yet implemented for numerical columns. Column '%s' is not numerical."
                % category)

        field = category
        colormapper_options = {"palette": colormap}
        if not colormap_range is None:
            if not isinstance(colormap_range, (tuple, list)):
                raise ValueError(
                    "<colormap_range> can only be 'None' or a tuple/list of form (min, max)."
                )
            elif len(colormap_range) == 2:
                colormapper_options["low"] = colormap_range[0]
                colormapper_options["high"] = colormap_range[1]
        else:
            colormapper_options["low"] = gdf[field].min()
            colormapper_options["high"] = gdf[field].max()
        if colormap_uselog:
            colormapper = LogColorMapper(**colormapper_options)
        else:
            colormapper = LinearColorMapper(**colormapper_options)
        kwargs["fill_color"] = {"field": "Colormap", "transform": colormapper}
        if not isinstance(legend, str):
            legend = str(field)

    elif not dropdown is None:
        # Check if all columns in dropdown selection are numerical:
        for col in dropdown:
            if not issubclass(gdf[col].dtype.type, np.number):
                raise NotImplementedError(
                    "<dropdown> plot only yet implemented for numerical columns. Column '%s' is not numerical."
                    % col)

        field = dropdown[0]
        colormapper_options = {"palette": colormap}
        if not colormap_range is None:
            if not isinstance(colormap_range, (tuple, list)):
                raise ValueError(
                    "<colormap_range> can only be 'None' or a tuple/list of form (min, max)."
                )
            elif len(colormap_range) == 2:
                colormapper_options["low"] = colormap_range[0]
                colormapper_options["high"] = colormap_range[1]
        else:
            colormapper_options["low"] = gdf[dropdown].min().min()
            colormapper_options["high"] = gdf[dropdown].max().max()
        if colormap_uselog:
            colormapper = LogColorMapper(**colormapper_options)
        else:
            colormapper = LinearColorMapper(**colormapper_options)
        kwargs["fill_color"] = {"field": "Colormap", "transform": colormapper}
        if not isinstance(legend, str):
            legend = "Geolayer"

    elif not slider is None:
        # Check if all columns in dropdown selection are numerical:
        for col in slider:
            if not issubclass(gdf[col].dtype.type, np.number):
                raise NotImplementedError(
                    "<slider> plot only yet implemented for numerical columns. Column '%s' is not numerical."
                    % col)

        field = slider[0]
        colormapper_options = {"palette": colormap}
        if not colormap_range is None:
            if not isinstance(colormap_range, (tuple, list)):
                raise ValueError(
                    "<colormap_range> can only be 'None' or a tuple/list of form (min, max)."
                )
            elif len(colormap_range) == 2:
                colormapper_options["low"] = colormap_range[0]
                colormapper_options["high"] = colormap_range[1]
        else:
            colormapper_options["low"] = gdf[slider].min().min()
            colormapper_options["high"] = gdf[slider].max().max()
        if colormap_uselog:
            colormapper = LogColorMapper(**colormapper_options)
        else:
            colormapper = LinearColorMapper(**colormapper_options)
        kwargs["fill_color"] = {"field": "Colormap", "transform": colormapper}
        if not isinstance(legend, str):
            legend = "Geolayer"

    # Check for Hovertool columns:
    if hovertool:
        if not isinstance(hovertool_columns, (list, tuple)):
            if hovertool_columns == "all":
                hovertool_columns = list(
                    filter(lambda col: col != "geometry", df_shapes.columns))
            else:
                raise ValueError(
                    "<hovertool_columns> has to be a list of columns of the GeoDataFrame or the string 'all'."
                )
        elif len(hovertool_columns) == 0:
            if not category is None:
                hovertool_columns = [category]
            elif not dropdown is None:
                hovertool_columns = dropdown
            elif not slider is None:
                hovertool_columns = slider
            else:
                hovertool_columns = []
        else:
            for col in hovertool_columns:
                if col not in gdf.columns:
                    raise ValueError(
                        "Could not find columns '%s' in GeoDataFrame. <hovertool_columns> has to be a list of columns of the GeoDataFrame or the string 'all'."
                        % col)
    else:
        if category is None:
            hovertool_columns = []
        else:
            hovertool_columns = [category]

    # Reduce DataFrame to needed columns:
    additional_columns = []
    for kwarg, value in kwargs.items():
        if isinstance(value, Hashable):
            if value in gdf.columns:
                additional_columns.append(value)
    if category_options == 0:
        gdf = gdf[list(set(hovertool_columns) | set(additional_columns)) +
                  ["geometry"]]
    else:
        gdf = gdf[list(
            set(hovertool_columns) | set(category_columns)
            | set(additional_columns)) + ["geometry"]]
        gdf["Colormap"] = gdf[field]
        field = "Colormap"

    # Create GeoJSON DataSource for Plot:
    geo_source = GeoJSONDataSource(geojson=gdf.to_json())

    # Draw Glyph on Figure:
    layout = None
    if "Point" in layertypes:
        if "line_color" not in kwargs:
            kwargs["line_color"] = kwargs["fill_color"]
        p.scatter(x="x", y="y", source=geo_source, legend=legend, **kwargs)

    if "Line" in layertypes:
        if "line_color" not in kwargs:
            kwargs["line_color"] = kwargs["fill_color"]
            del kwargs["fill_color"]
        p.multi_line(xs="xs",
                     ys="ys",
                     source=geo_source,
                     legend=legend,
                     **kwargs)

    if "Polygon" in layertypes:

        if "line_color" not in kwargs:
            kwargs["line_color"] = "black"

        # Plot polygons:
        p.patches(xs="xs", ys="ys", source=geo_source, legend=legend, **kwargs)

    if hovertool and (category_options == 1 or len(hovertool_columns) > 0):
        my_hover = HoverTool()
        my_hover.tooltips = [(str(col), "@{%s}" % col)
                             for col in hovertool_columns]
        p.add_tools(my_hover)

    if show_colorbar and category_options == 1:
        colorbar_options = {
            "color_mapper": colormapper,
            "label_standoff": 12,
            "border_line_color": None,
            "location": (0, 0),
        }
        if colormap_uselog:
            colorbar_options["ticker"] = LogTicker()

        colorbar = ColorBar(**colorbar_options)

        p.add_layout(colorbar, "right")

    if not dropdown is None:
        # Define Dropdown widget:
        dropdown_widget = Dropdown(label="Select Choropleth Layer",
                                   menu=list(zip(dropdown, dropdown)))

        # Define Callback for Dropdown widget:
        callback = CustomJS(
            args=dict(dropdown_widget=dropdown_widget,
                      geo_source=geo_source,
                      p=p),
            code="""

                //Change selection of field for Colormapper for choropleth plot:
                geo_source.data["Colormap"] = geo_source.data[dropdown_widget.value];
                geo_source.change.emit();
                //p.legend[0].items[0]["label"] = dropdown_widget.value;

                            """,
        )
        dropdown_widget.js_on_change("value", callback)

        # Add Dropdown widget above the plot:
        layout = column(dropdown_widget, p)

    if not slider is None:

        if slider_range is None:
            slider_start = 0
            slider_end = len(slider) - 1
            slider_step = 1

        value2name = ColumnDataSource({
            "Values":
            np.arange(slider_start, slider_end + slider_step, slider_step),
            "Names":
            slider,
        })

        # Define Slider widget:
        slider_widget = Slider(
            start=slider_start,
            end=slider_end,
            value=slider_start,
            step=slider_step,
            title=slider_name,
        )

        # Define Callback for Slider widget:
        callback = CustomJS(
            args=dict(
                slider_widget=slider_widget,
                geo_source=geo_source,
                value2name=value2name,
            ),
            code="""

                //Change selection of field for Colormapper for choropleth plot:
                var slider_value = slider_widget.value;
                for(i=0; i<value2name.data["Names"].length; i++)
                    {
                    if (value2name.data["Values"][i] == slider_value)
                        {
                         var name = value2name.data["Names"][i];
                         }

                    }
                geo_source.data["Colormap"] = geo_source.data[name];
                geo_source.change.emit();

                            """,
        )
        slider_widget.js_on_change("value", callback)

        # Add Slider widget above the plot:
        layout = column(slider_widget, p)

    # Set click policy for legend:
    p.legend.click_policy = "hide"

    # Display plot and if wanted return plot:
    if layout is None:
        layout = p

    # Display plot if wanted
    if show_figure:
        show(layout)

    # Return as (embeddable) HTML if wanted:
    if return_html:
        return embedded_html(layout)

    # Return plot:
    if return_figure:
        return layout
示例#33
0
       return v | 0
    }
    const color = source.data['color']
    const text_color = source.data['text_color']
    const R = toInt(red.value)
    const G = toInt(green.value)
    const B = toInt(blue.value)
    color[0] = rgbToHex(R, G, B)
    text_color[0] = '#ffffff'
    if ((R > 127) || (G > 127) || (B > 127)) {
        text_color[0] = '#000000'
    }
    source.change.emit()
""")

red_slider.js_on_change('value', callback)
blue_slider.js_on_change('value', callback)
green_slider.js_on_change('value', callback)

# plot 2: create a color spectrum with a hover-over tool to inspect hex codes

brightness = 0.8 # change to have brighter/darker colors
crx = list(range(1,1001)) # the resolution is 1000 colors
cry = [ 5 for i in range(len(crx)) ]
crcolor, crRGBs = generate_color_range(1000,brightness) # produce spectrum

# make data source object to allow information to be displayed by hover tool
crsource = ColumnDataSource(data=dict(x=crx, y=cry, crcolor=crcolor, RGBs=crRGBs))

# create second plot
p2 = figure(x_range=(0,1000), y_range=(0,10),
示例#34
0
def spectroscopy_plot(obj_id, user, spec_id=None, width=600, device="browser"):
    obj = Obj.query.get(obj_id)
    spectra = (
        DBSession()
        .query(Spectrum)
        .join(Obj)
        .join(GroupSpectrum)
        .filter(
            Spectrum.obj_id == obj_id,
            GroupSpectrum.group_id.in_([g.id for g in user.accessible_groups]),
        )
    ).all()

    if spec_id is not None:
        spectra = [spec for spec in spectra if spec.id == int(spec_id)]
    if len(spectra) == 0:
        return None, None, None

    rainbow = cm.get_cmap('rainbow', len(spectra))
    palette = list(map(rgb2hex, rainbow(range(len(spectra)))))
    color_map = dict(zip([s.id for s in spectra], palette))

    data = []
    for i, s in enumerate(spectra):

        # normalize spectra to a median flux of 1 for easy comparison
        normfac = np.nanmedian(np.abs(s.fluxes))
        normfac = normfac if normfac != 0.0 else 1e-20

        df = pd.DataFrame(
            {
                'wavelength': s.wavelengths,
                'flux': s.fluxes / normfac,
                'id': s.id,
                'telescope': s.instrument.telescope.name,
                'instrument': s.instrument.name,
                'date_observed': s.observed_at.isoformat(sep=' ', timespec='seconds'),
                'pi': (
                    s.assignment.run.pi
                    if s.assignment is not None
                    else (
                        s.followup_request.allocation.pi
                        if s.followup_request is not None
                        else ""
                    )
                ),
            }
        )
        data.append(df)
    data = pd.concat(data)

    data.sort_values(by=['date_observed', 'wavelength'], inplace=True)

    dfs = []
    for i, s in enumerate(spectra):
        # Smooth the spectrum by using a rolling average
        df = (
            pd.DataFrame({'wavelength': s.wavelengths, 'flux': s.fluxes})
            .rolling(2)
            .mean(numeric_only=True)
            .dropna()
        )
        dfs.append(df)

    smoothed_data = pd.concat(dfs)

    split = data.groupby('id', sort=False)

    hover = HoverTool(
        tooltips=[
            ('wavelength', '@wavelength{0,0.000}'),
            ('flux', '@flux'),
            ('telesecope', '@telescope'),
            ('instrument', '@instrument'),
            ('UTC date observed', '@date_observed'),
            ('PI', '@pi'),
        ]
    )
    smoothed_max = np.max(smoothed_data['flux'])
    smoothed_min = np.min(smoothed_data['flux'])
    ymax = smoothed_max * 1.05
    ymin = smoothed_min - 0.05 * (smoothed_max - smoothed_min)
    xmin = np.min(data['wavelength']) - 100
    xmax = np.max(data['wavelength']) + 100
    if obj.redshift is not None and obj.redshift > 0:
        xmin_rest = xmin / (1.0 + obj.redshift)
        xmax_rest = xmax / (1.0 + obj.redshift)

    active_drag = None if "mobile" in device or "tablet" in device else "box_zoom"
    tools = (
        "box_zoom, pan, reset"
        if "mobile" in device or "tablet" in device
        else "box_zoom,wheel_zoom,pan,reset"
    )

    # These values are equivalent from the photometry plot values
    frame_width = width - 64
    if device == "mobile_portrait":
        legend_items_per_row = 1
        legend_row_height = 24
        aspect_ratio = 1
    elif device == "mobile_landscape":
        legend_items_per_row = 4
        legend_row_height = 50
        aspect_ratio = 1.8
    elif device == "tablet_portrait":
        legend_items_per_row = 5
        legend_row_height = 50
        aspect_ratio = 1.5
    elif device == "tablet_landscape":
        legend_items_per_row = 7
        legend_row_height = 50
        aspect_ratio = 1.8
    elif device == "browser":
        frame_width = width - 200
    plot_height = (
        400
        if device == "browser"
        else math.floor(width / aspect_ratio)
        + legend_row_height * int(len(split) / legend_items_per_row)
        + 30  # 30 is the height of the toolbar
    )

    plot = figure(
        frame_width=frame_width,
        height=plot_height,
        y_range=(ymin, ymax),
        x_range=(xmin, xmax),
        tools=tools,
        toolbar_location="above",
        active_drag=active_drag,
    )
    plot.add_tools(hover)
    model_dict = {}
    legend_items = []
    for i, (key, df) in enumerate(split):
        renderers = []
        s = Spectrum.query.get(key)
        label = f'{s.instrument.name} ({s.observed_at.date().strftime("%m/%d/%y")})'
        model_dict['s' + str(i)] = plot.step(
            x='wavelength',
            y='flux',
            color=color_map[key],
            source=ColumnDataSource(df),
        )
        renderers.append(model_dict['s' + str(i)])
        legend_items.append(LegendItem(label=label, renderers=renderers))
        model_dict['l' + str(i)] = plot.line(
            x='wavelength',
            y='flux',
            color=color_map[key],
            source=ColumnDataSource(df),
            line_alpha=0.0,
        )
    plot.xaxis.axis_label = 'Wavelength (Å)'
    plot.yaxis.axis_label = 'Flux'
    plot.toolbar.logo = None
    if obj.redshift is not None and obj.redshift > 0:
        plot.extra_x_ranges = {"rest_wave": Range1d(start=xmin_rest, end=xmax_rest)}
        plot.add_layout(
            LinearAxis(x_range_name="rest_wave", axis_label="Rest Wavelength (Å)"),
            'above',
        )

    # TODO how to choose a good default?
    plot.y_range = Range1d(0, 1.03 * data.flux.max())

    legend_loc = "below" if "mobile" in device or "tablet" in device else "right"
    legend_orientation = (
        "vertical" if device in ["browser", "mobile_portrait"] else "horizontal"
    )

    add_plot_legend(plot, legend_items, width, legend_orientation, legend_loc)

    # 20 is for padding
    slider_width = width if "mobile" in device else int(width / 2) - 20
    z_title = Div(text="Redshift (<i>z</i>): ")
    z_slider = Slider(
        value=obj.redshift if obj.redshift is not None else 0.0,
        start=0.0,
        end=3.0,
        step=0.001,
        show_value=False,
        format="0[.]000",
    )
    z_textinput = TextInput(
        value=str(obj.redshift if obj.redshift is not None else 0.0)
    )
    z_slider.js_on_change(
        'value',
        CustomJS(
            args={'slider': z_slider, 'textinput': z_textinput},
            code="""
            textinput.value = parseFloat(slider.value).toFixed(3);
            textinput.change.emit();
        """,
        ),
    )
    z = column(
        z_title,
        z_slider,
        z_textinput,
        width=slider_width,
        margin=(4, 10, 0, 10),
    )

    v_title = Div(text="<i>V</i><sub>expansion</sub> (km/s): ")
    v_exp_slider = Slider(
        value=0.0,
        start=0.0,
        end=3e4,
        step=10.0,
        show_value=False,
    )
    v_exp_textinput = TextInput(value='0')
    v_exp_slider.js_on_change(
        'value',
        CustomJS(
            args={'slider': v_exp_slider, 'textinput': v_exp_textinput},
            code="""
            textinput.value = parseFloat(slider.value).toFixed(0);
            textinput.change.emit();
        """,
        ),
    )
    v_exp = column(
        v_title,
        v_exp_slider,
        v_exp_textinput,
        width=slider_width,
        margin=(0, 10, 0, 10),
    )

    for i, (wavelengths, color) in enumerate(SPEC_LINES.values()):
        el_data = pd.DataFrame({'wavelength': wavelengths})
        obj_redshift = 0 if obj.redshift is None else obj.redshift
        el_data['x'] = el_data['wavelength'] * (1.0 + obj_redshift)
        model_dict[f'el{i}'] = plot.segment(
            x0='x',
            x1='x',
            # TODO change limits
            y0=0,
            y1=1e4,
            color=color,
            source=ColumnDataSource(el_data),
        )
        model_dict[f'el{i}'].visible = False

    # Split spectral line legend into columns
    if device == "mobile_portrait":
        columns = 3
    elif device == "mobile_landscape":
        columns = 5
    else:
        columns = 7
    element_dicts = zip(*itertools.zip_longest(*[iter(SPEC_LINES.items())] * columns))

    elements_groups = []  # The Bokeh checkbox groups
    callbacks = []  # The checkbox callbacks for each element
    for column_idx, element_dict in enumerate(element_dicts):
        element_dict = [e for e in element_dict if e is not None]
        labels = [key for key, value in element_dict]
        colors = [c for key, (w, c) in element_dict]
        elements = CheckboxWithLegendGroup(
            labels=labels, active=[], colors=colors, width=width // (columns + 1)
        )
        elements_groups.append(elements)

        callback = CustomJS(
            args={
                'elements': elements,
                'z': z_textinput,
                'v_exp': v_exp_textinput,
                **model_dict,
            },
            code=f"""
            let c = 299792.458; // speed of light in km / s
            const i_max = {column_idx} +  {columns} * elements.labels.length;
            let local_i = 0;
            for (let i = {column_idx}; i < i_max; i = i + {columns}) {{
                let el = eval("el" + i);
                el.visible = (elements.active.includes(local_i))
                el.data_source.data.x = el.data_source.data.wavelength.map(
                    x_i => (x_i * (1 + parseFloat(z.value)) /
                                    (1 + parseFloat(v_exp.value) / c))
                );
                el.data_source.change.emit();
                local_i++;
            }}
        """,
        )
        elements.js_on_click(callback)
        callbacks.append(callback)

    z_textinput.js_on_change(
        'value',
        CustomJS(
            args={
                'z': z_textinput,
                'slider': z_slider,
                'v_exp': v_exp_textinput,
                **model_dict,
            },
            code="""
            // Update slider value to match text input
            slider.value = parseFloat(z.value).toFixed(3);
        """,
        ),
    )

    v_exp_textinput.js_on_change(
        'value',
        CustomJS(
            args={
                'z': z_textinput,
                'slider': v_exp_slider,
                'v_exp': v_exp_textinput,
                **model_dict,
            },
            code="""
            // Update slider value to match text input
            slider.value = parseFloat(v_exp.value).toFixed(3);
        """,
        ),
    )

    # Update the element spectral lines as well
    for callback in callbacks:
        z_textinput.js_on_change('value', callback)
        v_exp_textinput.js_on_change('value', callback)

    # Add some height for the checkboxes and sliders
    if device == "mobile_portrait":
        height = plot_height + 400
    elif device == "mobile_landscape":
        height = plot_height + 350
    else:
        height = plot_height + 200

    row2 = row(elements_groups)
    row3 = column(z, v_exp) if "mobile" in device else row(z, v_exp)
    layout = column(
        plot,
        row2,
        row3,
        sizing_mode='stretch_width',
        width=width,
        height=height,
    )
    return bokeh_embed.json_item(layout)
示例#35
0
offset_slider = Slider(start=-5, end=5, value=0, step=.1, title="Offset")

callback = CustomJS(args=dict(source=source, amp=amp_slider, freq=freq_slider, phase=phase_slider, offset=offset_slider),
                    code="""
    const data = source.data;
    const A = amp.value;
    const k = freq.value;
    const phi = phase.value;
    const B = offset.value;
    const x = data['x']
    const y = data['y']
    for (var i = 0; i < x.length; i++) {
        y[i] = B + A*Math.sin(k*x[i]+phi);
    }
    source.change.emit();
""")

amp_slider.js_on_change('value', callback)
freq_slider.js_on_change('value', callback)
phase_slider.js_on_change('value', callback)
offset_slider.js_on_change('value', callback)

layout = row(
    plot,
    column(amp_slider, freq_slider, phase_slider, offset_slider),
)

output_file("slider.html", title="slider.py example")

show(layout)
示例#36
0
def photometry_plot(obj_id, user, width=600, device="browser"):
    """Create object photometry scatter plot.

    Parameters
    ----------
    obj_id : str
        ID of Obj to be plotted.

    Returns
    -------
    dict
        Returns Bokeh JSON embedding for the desired plot.
    """

    data = pd.read_sql(
        DBSession()
        .query(
            Photometry,
            Telescope.nickname.label("telescope"),
            Instrument.name.label("instrument"),
        )
        .join(Instrument, Instrument.id == Photometry.instrument_id)
        .join(Telescope, Telescope.id == Instrument.telescope_id)
        .filter(Photometry.obj_id == obj_id)
        .filter(
            Photometry.groups.any(Group.id.in_([g.id for g in user.accessible_groups]))
        )
        .statement,
        DBSession().bind,
    )

    if data.empty:
        return None, None, None

    # get spectra to annotate on phot plots
    spectra = (
        Spectrum.query_records_accessible_by(user)
        .filter(Spectrum.obj_id == obj_id)
        .all()
    )

    data['color'] = [get_color(f) for f in data['filter']]

    # get marker for each unique instrument
    instruments = list(data.instrument.unique())
    markers = []
    for i, inst in enumerate(instruments):
        markers.append(phot_markers[i % len(phot_markers)])

    filters = list(set(data['filter']))
    colors = [get_color(f) for f in filters]

    color_mapper = CategoricalColorMapper(factors=filters, palette=colors)
    color_dict = {'field': 'filter', 'transform': color_mapper}

    labels = []
    for i, datarow in data.iterrows():
        label = f'{datarow["instrument"]}/{datarow["filter"]}'
        if datarow['origin'] is not None:
            label += f'/{datarow["origin"]}'
        labels.append(label)

    data['label'] = labels
    data['zp'] = PHOT_ZP
    data['magsys'] = 'ab'
    data['alpha'] = 1.0
    data['lim_mag'] = (
        -2.5 * np.log10(data['fluxerr'] * PHOT_DETECTION_THRESHOLD) + data['zp']
    )

    # Passing a dictionary to a bokeh datasource causes the frontend to die,
    # deleting the dictionary column fixes that
    del data['original_user_data']

    # keep track of things that are only upper limits
    data['hasflux'] = ~data['flux'].isna()

    # calculate the magnitudes - a photometry point is considered "significant"
    # or "detected" (and thus can be represented by a magnitude) if its snr
    # is above PHOT_DETECTION_THRESHOLD
    obsind = data['hasflux'] & (
        data['flux'].fillna(0.0) / data['fluxerr'] >= PHOT_DETECTION_THRESHOLD
    )
    data.loc[~obsind, 'mag'] = None
    data.loc[obsind, 'mag'] = -2.5 * np.log10(data[obsind]['flux']) + PHOT_ZP

    # calculate the magnitude errors using standard error propagation formulae
    # https://en.wikipedia.org/wiki/Propagation_of_uncertainty#Example_formulae
    data.loc[~obsind, 'magerr'] = None
    coeff = 2.5 / np.log(10)
    magerrs = np.abs(coeff * data[obsind]['fluxerr'] / data[obsind]['flux'])
    data.loc[obsind, 'magerr'] = magerrs
    data['obs'] = obsind
    data['stacked'] = False

    split = data.groupby('label', sort=False)

    finite = np.isfinite(data['flux'])
    fdata = data[finite]
    lower = np.min(fdata['flux']) * 0.95
    upper = np.max(fdata['flux']) * 1.05

    xmin = data['mjd'].min() - 2
    xmax = data['mjd'].max() + 2

    # Layout parameters based on device type
    active_drag = None if "mobile" in device or "tablet" in device else "box_zoom"
    tools = (
        'box_zoom,pan,reset'
        if "mobile" in device or "tablet" in device
        else "box_zoom,wheel_zoom,pan,reset,save"
    )
    legend_loc = "below" if "mobile" in device or "tablet" in device else "right"
    legend_orientation = (
        "vertical" if device in ["browser", "mobile_portrait"] else "horizontal"
    )

    # Compute a plot component height based on rough number of legend rows added below the plot
    # Values are based on default sizing of bokeh components and an estimate of how many
    # legend items would fit on the average device screen. Note that the legend items per
    # row is computed more exactly later once labels are extracted from the data (with the
    # add_plot_legend() function).
    #
    # The height is manually computed like this instead of using built in aspect_ratio/sizing options
    # because with the new Interactive Legend approach (instead of the legacy CheckboxLegendGroup), the
    # Legend component is considered part of the plot and plays into the sizing computations. Since the
    # number of items in the legend can alter the needed heights of the plot, using built-in Bokeh options
    # for sizing does not allow for keeping the actual graph part of the plot at a consistent aspect ratio.
    #
    # For the frame width, by default we take the desired plot width minus 64 for the y-axis/label taking
    # up horizontal space
    frame_width = width - 64
    if device == "mobile_portrait":
        legend_items_per_row = 1
        legend_row_height = 24
        aspect_ratio = 1
    elif device == "mobile_landscape":
        legend_items_per_row = 4
        legend_row_height = 50
        aspect_ratio = 1.8
    elif device == "tablet_portrait":
        legend_items_per_row = 5
        legend_row_height = 50
        aspect_ratio = 1.5
    elif device == "tablet_landscape":
        legend_items_per_row = 7
        legend_row_height = 50
        aspect_ratio = 1.8
    elif device == "browser":
        # Width minus some base width for the legend, which is only a column to the right
        # for browser mode
        frame_width = width - 200

    height = (
        500
        if device == "browser"
        else math.floor(width / aspect_ratio)
        + legend_row_height * int(len(split) / legend_items_per_row)
        + 30  # 30 is the height of the toolbar
    )

    plot = figure(
        frame_width=frame_width,
        height=height,
        active_drag=active_drag,
        tools=tools,
        toolbar_location='above',
        toolbar_sticky=True,
        y_range=(lower, upper),
        min_border_right=16,
        x_axis_location='above',
        sizing_mode="stretch_width",
    )

    plot.xaxis.axis_label = 'MJD'
    now = Time.now().mjd
    plot.extra_x_ranges = {"Days Ago": Range1d(start=now - xmin, end=now - xmax)}
    plot.add_layout(LinearAxis(x_range_name="Days Ago", axis_label="Days Ago"), 'below')

    imhover = HoverTool(tooltips=tooltip_format)
    imhover.renderers = []
    plot.add_tools(imhover)

    model_dict = {}
    legend_items = []
    for i, (label, sdf) in enumerate(split):
        renderers = []

        # for the flux plot, we only show things that have a flux value
        df = sdf[sdf['hasflux']]

        key = f'obs{i}'
        model_dict[key] = plot.scatter(
            x='mjd',
            y='flux',
            color='color',
            marker=factor_mark('instrument', markers, instruments),
            fill_color=color_dict,
            alpha='alpha',
            source=ColumnDataSource(df),
        )
        renderers.append(model_dict[key])
        imhover.renderers.append(model_dict[key])

        key = f'bin{i}'
        model_dict[key] = plot.scatter(
            x='mjd',
            y='flux',
            color='color',
            marker=factor_mark('instrument', markers, instruments),
            fill_color=color_dict,
            source=ColumnDataSource(
                data=dict(
                    mjd=[],
                    flux=[],
                    fluxerr=[],
                    filter=[],
                    color=[],
                    lim_mag=[],
                    mag=[],
                    magerr=[],
                    stacked=[],
                    instrument=[],
                )
            ),
        )
        renderers.append(model_dict[key])
        imhover.renderers.append(model_dict[key])

        key = 'obserr' + str(i)
        y_err_x = []
        y_err_y = []

        for d, ro in df.iterrows():
            px = ro['mjd']
            py = ro['flux']
            err = ro['fluxerr']

            y_err_x.append((px, px))
            y_err_y.append((py - err, py + err))

        model_dict[key] = plot.multi_line(
            xs='xs',
            ys='ys',
            color='color',
            alpha='alpha',
            source=ColumnDataSource(
                data=dict(
                    xs=y_err_x, ys=y_err_y, color=df['color'], alpha=[1.0] * len(df)
                )
            ),
        )
        renderers.append(model_dict[key])

        key = f'binerr{i}'
        model_dict[key] = plot.multi_line(
            xs='xs',
            ys='ys',
            color='color',
            # legend_label=label,
            source=ColumnDataSource(data=dict(xs=[], ys=[], color=[])),
        )
        renderers.append(model_dict[key])

        legend_items.append(LegendItem(label=label, renderers=renderers))

    if device == "mobile_portrait":
        plot.xaxis.ticker.desired_num_ticks = 5
    plot.yaxis.axis_label = 'Flux (μJy)'
    plot.toolbar.logo = None

    add_plot_legend(plot, legend_items, width, legend_orientation, legend_loc)
    slider = Slider(
        start=0.0,
        end=15.0,
        value=0.0,
        step=1.0,
        title='Binsize (days)',
        max_width=350,
        margin=(4, 10, 0, 10),
    )

    callback = CustomJS(
        args={'slider': slider, 'n_labels': len(split), **model_dict},
        code=open(
            os.path.join(os.path.dirname(__file__), '../static/js/plotjs', 'stackf.js')
        )
        .read()
        .replace('default_zp', str(PHOT_ZP))
        .replace('detect_thresh', str(PHOT_DETECTION_THRESHOLD)),
    )

    slider.js_on_change('value', callback)

    # Mark the first and last detections
    detection_dates = data[data['hasflux']]['mjd']
    if len(detection_dates) > 0:
        first = round(detection_dates.min(), 6)
        last = round(detection_dates.max(), 6)
        first_color = "#34b4eb"
        last_color = "#8992f5"
        midpoint = (upper + lower) / 2
        line_top = 5 * upper - 4 * midpoint
        line_bottom = 5 * lower - 4 * midpoint
        y = np.linspace(line_bottom, line_top, num=5000)
        first_r = plot.line(
            x=np.full(5000, first),
            y=y,
            line_alpha=0.5,
            line_color=first_color,
            line_width=2,
        )
        plot.add_tools(
            HoverTool(
                tooltips=[("First detection", f'{first}')],
                renderers=[first_r],
            )
        )
        last_r = plot.line(
            x=np.full(5000, last),
            y=y,
            line_alpha=0.5,
            line_color=last_color,
            line_width=2,
        )
        plot.add_tools(
            HoverTool(
                tooltips=[("Last detection", f'{last}')],
                renderers=[last_r],
            )
        )

    # Mark when spectra were taken
    annotate_spec(plot, spectra, lower, upper)
    layout = column(slider, plot, width=width, height=height)

    p1 = Panel(child=layout, title='Flux')

    # now make the mag light curve
    ymax = (
        np.nanmax(
            (
                np.nanmax(data.loc[obsind, 'mag']) if any(obsind) else np.nan,
                np.nanmax(data.loc[~obsind, 'lim_mag']) if any(~obsind) else np.nan,
            )
        )
        + 0.1
    )
    ymin = (
        np.nanmin(
            (
                np.nanmin(data.loc[obsind, 'mag']) if any(obsind) else np.nan,
                np.nanmin(data.loc[~obsind, 'lim_mag']) if any(~obsind) else np.nan,
            )
        )
        - 0.1
    )

    plot = figure(
        frame_width=frame_width,
        height=height,
        active_drag=active_drag,
        tools=tools,
        y_range=(ymax, ymin),
        x_range=(xmin, xmax),
        toolbar_location='above',
        toolbar_sticky=True,
        x_axis_location='above',
        sizing_mode="stretch_width",
    )

    plot.xaxis.axis_label = 'MJD'
    now = Time.now().mjd
    plot.extra_x_ranges = {"Days Ago": Range1d(start=now - xmin, end=now - xmax)}
    plot.add_layout(LinearAxis(x_range_name="Days Ago", axis_label="Days Ago"), 'below')

    obj = DBSession().query(Obj).get(obj_id)
    if obj.dm is not None:
        plot.extra_y_ranges = {
            "Absolute Mag": Range1d(start=ymax - obj.dm, end=ymin - obj.dm)
        }
        plot.add_layout(
            LinearAxis(y_range_name="Absolute Mag", axis_label="m - DM"), 'right'
        )

    # Mark the first and last detections again
    detection_dates = data[obsind]['mjd']
    if len(detection_dates) > 0:
        first = round(detection_dates.min(), 6)
        last = round(detection_dates.max(), 6)
        midpoint = (ymax + ymin) / 2
        line_top = 5 * ymax - 4 * midpoint
        line_bottom = 5 * ymin - 4 * midpoint
        y = np.linspace(line_bottom, line_top, num=5000)
        first_r = plot.line(
            x=np.full(5000, first),
            y=y,
            line_alpha=0.5,
            line_color=first_color,
            line_width=2,
        )
        plot.add_tools(
            HoverTool(
                tooltips=[("First detection", f'{first}')],
                renderers=[first_r],
            )
        )
        last_r = plot.line(
            x=np.full(5000, last),
            y=y,
            line_alpha=0.5,
            line_color=last_color,
            line_width=2,
        )
        plot.add_tools(
            HoverTool(
                tooltips=[("Last detection", f'{last}')],
                renderers=[last_r],
                point_policy='follow_mouse',
            )
        )

    # Mark when spectra were taken
    annotate_spec(plot, spectra, ymax, ymin)

    imhover = HoverTool(tooltips=tooltip_format)
    imhover.renderers = []
    plot.add_tools(imhover)

    model_dict = {}

    # Legend items are individually stored instead of being applied
    # directly when plotting so that they can be separated into multiple
    # Legend() components if needed (to simulate horizontal row wrapping).
    # This is necessary because Bokeh does not support row wrapping with
    # horizontally-oriented legends out-of-the-box.
    legend_items = []
    for i, (label, df) in enumerate(split):
        renderers = []

        key = f'obs{i}'
        model_dict[key] = plot.scatter(
            x='mjd',
            y='mag',
            color='color',
            marker=factor_mark('instrument', markers, instruments),
            fill_color=color_dict,
            alpha='alpha',
            source=ColumnDataSource(df[df['obs']]),
        )
        renderers.append(model_dict[key])
        imhover.renderers.append(model_dict[key])

        unobs_source = df[~df['obs']].copy()
        unobs_source.loc[:, 'alpha'] = 0.8

        key = f'unobs{i}'
        model_dict[key] = plot.scatter(
            x='mjd',
            y='lim_mag',
            color=color_dict,
            marker='inverted_triangle',
            fill_color='white',
            line_color='color',
            alpha='alpha',
            source=ColumnDataSource(unobs_source),
        )
        renderers.append(model_dict[key])
        imhover.renderers.append(model_dict[key])

        key = f'bin{i}'
        model_dict[key] = plot.scatter(
            x='mjd',
            y='mag',
            color=color_dict,
            marker=factor_mark('instrument', markers, instruments),
            fill_color='color',
            source=ColumnDataSource(
                data=dict(
                    mjd=[],
                    flux=[],
                    fluxerr=[],
                    filter=[],
                    color=[],
                    lim_mag=[],
                    mag=[],
                    magerr=[],
                    instrument=[],
                    stacked=[],
                )
            ),
        )
        renderers.append(model_dict[key])
        imhover.renderers.append(model_dict[key])

        key = 'obserr' + str(i)
        y_err_x = []
        y_err_y = []

        for d, ro in df[df['obs']].iterrows():
            px = ro['mjd']
            py = ro['mag']
            err = ro['magerr']

            y_err_x.append((px, px))
            y_err_y.append((py - err, py + err))

        model_dict[key] = plot.multi_line(
            xs='xs',
            ys='ys',
            color='color',
            alpha='alpha',
            source=ColumnDataSource(
                data=dict(
                    xs=y_err_x,
                    ys=y_err_y,
                    color=df[df['obs']]['color'],
                    alpha=[1.0] * len(df[df['obs']]),
                )
            ),
        )
        renderers.append(model_dict[key])

        key = f'binerr{i}'
        model_dict[key] = plot.multi_line(
            xs='xs',
            ys='ys',
            color='color',
            source=ColumnDataSource(data=dict(xs=[], ys=[], color=[])),
        )
        renderers.append(model_dict[key])

        key = f'unobsbin{i}'
        model_dict[key] = plot.scatter(
            x='mjd',
            y='lim_mag',
            color='color',
            marker='inverted_triangle',
            fill_color='white',
            line_color=color_dict,
            alpha=0.8,
            source=ColumnDataSource(
                data=dict(
                    mjd=[],
                    flux=[],
                    fluxerr=[],
                    filter=[],
                    color=[],
                    lim_mag=[],
                    mag=[],
                    magerr=[],
                    instrument=[],
                    stacked=[],
                )
            ),
        )
        imhover.renderers.append(model_dict[key])
        renderers.append(model_dict[key])

        key = f'all{i}'
        model_dict[key] = ColumnDataSource(df)

        key = f'bold{i}'
        model_dict[key] = ColumnDataSource(
            df[
                [
                    'mjd',
                    'flux',
                    'fluxerr',
                    'mag',
                    'magerr',
                    'filter',
                    'zp',
                    'magsys',
                    'lim_mag',
                    'stacked',
                ]
            ]
        )

        legend_items.append(LegendItem(label=label, renderers=renderers))

    add_plot_legend(plot, legend_items, width, legend_orientation, legend_loc)

    plot.yaxis.axis_label = 'AB mag'
    plot.toolbar.logo = None

    slider = Slider(
        start=0.0,
        end=15.0,
        value=0.0,
        step=1.0,
        title='Binsize (days)',
        max_width=350,
        margin=(4, 10, 0, 10),
    )

    button = Button(label="Export Bold Light Curve to CSV")
    button.js_on_click(
        CustomJS(
            args={'slider': slider, 'n_labels': len(split), **model_dict},
            code=open(
                os.path.join(
                    os.path.dirname(__file__), '../static/js/plotjs', "download.js"
                )
            )
            .read()
            .replace('objname', obj_id)
            .replace('default_zp', str(PHOT_ZP)),
        )
    )

    # Don't need to expose CSV download on mobile
    top_layout = (
        slider if "mobile" in device or "tablet" in device else row(slider, button)
    )

    callback = CustomJS(
        args={'slider': slider, 'n_labels': len(split), **model_dict},
        code=open(
            os.path.join(os.path.dirname(__file__), '../static/js/plotjs', 'stackm.js')
        )
        .read()
        .replace('default_zp', str(PHOT_ZP))
        .replace('detect_thresh', str(PHOT_DETECTION_THRESHOLD)),
    )
    slider.js_on_change('value', callback)
    layout = column(top_layout, plot, width=width, height=height)

    p2 = Panel(child=layout, title='Mag')

    # now make period plot

    # get periods from annotations
    annotation_list = obj.get_annotations_readable_by(user)
    period_labels = []
    period_list = []
    for an in annotation_list:
        if 'period' in an.data:
            period_list.append(an.data['period'])
            period_labels.append(an.origin + ": %.9f" % an.data['period'])

    if len(period_list) > 0:
        period = period_list[0]
    else:
        period = None
    # don't generate if no period annotated
    if period is not None:
        # bokeh figure for period plotting
        period_plot = figure(
            frame_width=frame_width,
            height=height,
            active_drag=active_drag,
            tools=tools,
            y_range=(ymax, ymin),
            x_range=(-0.01, 2.01),  # initially one phase
            toolbar_location='above',
            toolbar_sticky=False,
            x_axis_location='below',
            sizing_mode="stretch_width",
        )

        # axis labels
        period_plot.xaxis.axis_label = 'phase'
        period_plot.yaxis.axis_label = 'mag'
        period_plot.toolbar.logo = None

        # do we have a distance modulus (dm)?
        obj = DBSession().query(Obj).get(obj_id)
        if obj.dm is not None:
            period_plot.extra_y_ranges = {
                "Absolute Mag": Range1d(start=ymax - obj.dm, end=ymin - obj.dm)
            }
            period_plot.add_layout(
                LinearAxis(y_range_name="Absolute Mag", axis_label="m - DM"), 'right'
            )

        # initiate hover tool
        period_imhover = HoverTool(tooltips=tooltip_format)
        period_imhover.renderers = []
        period_plot.add_tools(period_imhover)

        # initiate period radio buttons
        period_selection = RadioGroup(labels=period_labels, active=0)

        phase_selection = RadioGroup(labels=["One phase", "Two phases"], active=1)

        # store all the plot data
        period_model_dict = {}

        # iterate over each filter
        legend_items = []
        for i, (label, df) in enumerate(split):
            renderers = []
            # fold x-axis on period in days
            df['mjd_folda'] = (df['mjd'] % period) / period
            df['mjd_foldb'] = df['mjd_folda'] + 1.0

            # phase plotting
            for ph in ['a', 'b']:
                key = 'fold' + ph + f'{i}'
                period_model_dict[key] = period_plot.scatter(
                    x='mjd_fold' + ph,
                    y='mag',
                    color='color',
                    marker=factor_mark('instrument', markers, instruments),
                    fill_color=color_dict,
                    alpha='alpha',
                    # visible=('a' in ph),
                    source=ColumnDataSource(df[df['obs']]),  # only visible data
                )
                # add to hover tool
                period_imhover.renderers.append(period_model_dict[key])
                renderers.append(period_model_dict[key])

                # errorbars for phases
                key = 'fold' + ph + f'err{i}'
                y_err_x = []
                y_err_y = []

                # get each visible error value
                for d, ro in df[df['obs']].iterrows():
                    px = ro['mjd_fold' + ph]
                    py = ro['mag']
                    err = ro['magerr']
                    # set up error tuples
                    y_err_x.append((px, px))
                    y_err_y.append((py - err, py + err))
                # plot phase errors
                period_model_dict[key] = period_plot.multi_line(
                    xs='xs',
                    ys='ys',
                    color='color',
                    alpha='alpha',
                    # visible=('a' in ph),
                    source=ColumnDataSource(
                        data=dict(
                            xs=y_err_x,
                            ys=y_err_y,
                            color=df[df['obs']]['color'],
                            alpha=[1.0] * len(df[df['obs']]),
                        )
                    ),
                )
                renderers.append(period_model_dict[key])

            legend_items.append(LegendItem(label=label, renderers=renderers))

        add_plot_legend(
            period_plot, legend_items, width, legend_orientation, legend_loc
        )

        # set up period adjustment text box
        period_title = Div(text="Period (days): ")
        period_textinput = TextInput(value=str(period if period is not None else 0.0))
        period_textinput.js_on_change(
            'value',
            CustomJS(
                args={
                    'textinput': period_textinput,
                    'numphases': phase_selection,
                    'n_labels': len(split),
                    'p': period_plot,
                    **period_model_dict,
                },
                code=open(
                    os.path.join(
                        os.path.dirname(__file__), '../static/js/plotjs', 'foldphase.js'
                    )
                ).read(),
            ),
        )
        # a way to modify the period
        period_double_button = Button(label="*2", width=30)
        period_double_button.js_on_click(
            CustomJS(
                args={'textinput': period_textinput},
                code="""
                const period = parseFloat(textinput.value);
                textinput.value = parseFloat(2.*period).toFixed(9);
                """,
            )
        )
        period_halve_button = Button(label="/2", width=30)
        period_halve_button.js_on_click(
            CustomJS(
                args={'textinput': period_textinput},
                code="""
                        const period = parseFloat(textinput.value);
                        textinput.value = parseFloat(period/2.).toFixed(9);
                        """,
            )
        )
        # a way to select the period
        period_selection.js_on_click(
            CustomJS(
                args={'textinput': period_textinput, 'periods': period_list},
                code="""
                textinput.value = parseFloat(periods[this.active]).toFixed(9);
                """,
            )
        )
        phase_selection.js_on_click(
            CustomJS(
                args={
                    'textinput': period_textinput,
                    'numphases': phase_selection,
                    'n_labels': len(split),
                    'p': period_plot,
                    **period_model_dict,
                },
                code=open(
                    os.path.join(
                        os.path.dirname(__file__), '../static/js/plotjs', 'foldphase.js'
                    )
                ).read(),
            )
        )

        # layout
        if device == "mobile_portrait":
            period_controls = column(
                row(
                    period_title,
                    period_textinput,
                    period_double_button,
                    period_halve_button,
                    width=width,
                    sizing_mode="scale_width",
                ),
                phase_selection,
                period_selection,
                width=width,
            )
            # Add extra height to plot based on period control components added
            # 18 is the height of each period selection radio option (per default font size)
            # and the 130 encompasses the other components which are consistent no matter
            # the data size.
            height += 130 + 18 * len(period_labels)
        else:
            period_controls = column(
                row(
                    period_title,
                    period_textinput,
                    period_double_button,
                    period_halve_button,
                    phase_selection,
                    width=width,
                    sizing_mode="scale_width",
                ),
                period_selection,
                margin=10,
            )
            # Add extra height to plot based on period control components added
            # Numbers are derived in similar manner to the "mobile_portrait" case above
            height += 90 + 18 * len(period_labels)

        period_layout = column(period_plot, period_controls, width=width, height=height)

        # Period panel
        p3 = Panel(child=period_layout, title='Period')

        # tabs for mag, flux, period
        tabs = Tabs(tabs=[p2, p1, p3], width=width, height=height, sizing_mode='fixed')
    else:
        # tabs for mag, flux
        tabs = Tabs(tabs=[p2, p1], width=width, height=height + 90, sizing_mode='fixed')
    return bokeh_embed.json_item(tabs)
from bokeh.models import CustomJS, ColumnDataSource, Slider
from bokeh.plotting import Figure, output_file, show

output_file("js_on_change.html")

x = [x*0.005 for x in range(0, 200)]
y = x

source = ColumnDataSource(data=dict(x=x, y=y))

plot = Figure(plot_width=400, plot_height=400)
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6)

callback = CustomJS(args=dict(source=source), code="""
    var data = source.data;
    var f = cb_obj.value
    x = data['x']
    y = data['y']
    for (i = 0; i < x.length; i++) {
        y[i] = Math.pow(x[i], f)
    }
    source.trigger('change');
""")

slider = Slider(start=0.1, end=4, value=1, step=.1, title="power")
slider.js_on_change('value', callback)

layout = column(slider, plot)

show(layout)
from bokeh.io import output_file, show
from bokeh.layouts import column
from bokeh.models import CustomJS, Slider, Div

para = Div(text="<h1>Slider Values:</h1><p>Slider 1: 0<p>Slider 2: 0<p>Slider 3: 0")

s1 = Slider(title="Slider 1 (Continuous)", start=0, end=1000, value=0, step=1,
            callback_policy="continuous")
s2 = Slider(title="Slider 2 (Throttle)", start=0, end=1000, value=0, step=1,
            callback_policy="throttle", callback_throttle=1000)
s3 = Slider(title="Slider 3 (Mouse Up)", start=0, end=1000, value=0, step=1,
            callback_policy="mouseup")

callback = CustomJS(args=dict(para=para, s1=s1, s2=s2, s3=s3), code="""
    para.text = "<h1>Slider Values</h1><p>Slider 1: " + s1.value  + "<p>Slider 2: " + s2.value + "<p>Slider 3: " + s3.value
""")

s1.js_on_change('value_throttled', callback)
s2.js_on_change('value_throttled', callback)
s3.js_on_change('value_throttled', callback)

output_file('slider_callback_policy.html')

show(column(s1, s2, s3, para))