Bokeh Map not displaying - JavaScript Error

When I attempt to run my notebook the map (bokeh plot) no longer displays on Binder but it does on my local machine (step 3 – select the one file available in data via the widget in step 1 ‘/data/alb_ppp_2020.tif’)

When I run in lab I get the following error

Javascript Error: Cannot read properties of undefined (reading 'set_value')

density lab

When run in notebook, I get no error the map just does not display
Population Density

Please advise

There is an interactive Bokeh that presently works in the classic notebook interface in launches from your instance after running %pip install sklearn first: here.
I note it uses interactive() (see below) and so trying to see if it is adaptable to fix your stuff.

I suspect your issue is related to the issue I think am seeing with ipywidget’s interact() and current MyBinder launches. However, Bokeh is much different & just switching to interactive as a work-around wasn’t enough, like it was in those other cases. Although, oddly that change was enough to get your size-adjusting and title widgets to display, but the plot wouldn’t.

So seeing that one here work that seems to use modern ipywidgets practices as evidenced by the use of output_figure = widgets.Output(), I wonder if it could be adapted. (You may wish to see here & note ipywidgets now need an output widget capturing the output to display.)

Below results in close to what you had and works at some level. I place this code in place of your plotting cell.
HUGE, STRANGE CAVEAT: It is super glitchy though. To get it to work first I have to try running it in JupyterLab and see the error that will be like Javascript Error: Error rendering Bokeh model: could not find #9ee3112f-b0fa-478a-86f5-f925a328c52b HTML tag, then stop the kernel, and run it in the classic interface, ignoring the error May also see NameError: name ‘mapper’ is not defined` that pops up the first time or two. Then it will sort of work by finally showing a plot. Usually shows the plot twice when cell first runs, but when you change the size or text it only loads one plot at that point.
Widgets work, but for some reason they show their values as text near them during the preparing stage. I don’t know why:

hide_code()
run_code()
from ipywidgets import interactive
from IPython.display import display, clear_output

"""Define callback function for the UI"""
def change_size(x):
    """This function is executed when a dropdown value is changed.
    It creates a new figure according to the new dropdown values."""
    p = heatmap(x,title.value)
    fig[0] = p
     
    with output_figure:
        clear_output(True)
        show(fig[0])
    fig[0]=p
     
    return x
 
def change_title(x):
    """This function is executed when a dropdown value is changed.
    It creates a new figure according to the new dropdown values."""
    p = heatmap(size.value,x)
    fig[0] = p
     
    with output_figure:
        clear_output(True)
        show(fig[0])
    fig[0]=p
     
    return x

def create_figure(x_var, y_var, data):
    """This is a helper function that creates a new figure and 
    plots values from all three species. x_var and y_var control
    the features on each axis."""
    species_colors=['coral', 'deepskyblue', 'darkblue']
    p = figure(title="",
               x_axis_label=x_var,
               y_axis_label=y_var)
    species_nr = 0
    for species in iris['target_names']:
        curr_dtps = data['target'] == species_nr
        circle = p.circle(
            data[x_var][curr_dtps],
            data[y_var][curr_dtps],
            line_width=2,
            color=species_colors[species_nr],
            name=species
            )
        species_nr += 1
    return p

def heatmap(size, title):

        p2 = figure(plot_width=size, plot_height=size, title=title,
                    x_range=(bbox2[0][0], bbox2[1][0]),y_range=(bbox2[0][1], bbox2[1][1]),
                    x_axis_type="mercator", y_axis_type="mercator")
        p2.title.text_font_size = '20pt'
        map_base = p2.add_tile(tile_provider2)
        map_base.level ='underlay'
        #convert source to selected dictionary value
        source = ColumnDataSource(viz_table)

        p2.rect(x='web_lon', y='web_lat', width=3500, height=3500, source=source,
                line_color=None, fill_color= transform("Population", mapper), alpha=0.07)

        p2.add_layout(color_bar, 'right')

        #show(p2)
        return p2
    
# The output widget is where we direct our figures
output_figure = widgets.Output()

if pop_file.value == "Select File":
    print("Waiting for Input")
else: 
    print("Preparing Plot...")
    data_store =pd.HDFStore(".\data\density.h5")
    
    viz_table = data_store["visuals"]                            
    
    lat_min = viz_table["latitude"].min()
    lat_max = viz_table["latitude"].max()
    lon_min = viz_table["longitude"].min()
    lon_max = viz_table["longitude"].max()
    
    
    #Have to convert to 2 decimal places otherwise to dense
    #round longitude
    viz_table["longitude"] = viz_table["longitude"].round(2)

    #round latitude
    viz_table["latitude"] = viz_table["latitude"].round(2)
    
       
    viz_table = viz_table.groupby(['longitude','latitude']).sum().reset_index()
    
    #Clear redundant lats/longs
    viz_table =viz_table.drop_duplicates(subset=["longitude", "latitude"], ignore_index=True)  
      
    viz_table['web_lon'], viz_table["web_lat"]  = transformer.transform(viz_table["latitude"].values, 
                                                                                      viz_table["longitude"].values)
    #grouped_poptable2 = grouped_poptable2[grouped_poptable2["Population"]!=0]
    new_total2 = round(sum(viz_table["Population"]))
        
    data_store["visuals"] = viz_table
    
    min_max_pts = [(lat_min, lon_min), (lat_max, lon_max)]
    bbox2 = []
    for pt in transformer.itransform(min_max_pts): 
        bbox2.append(pt)   

    size = widgets.IntText(
        value=600,
        description='Plot Size',
        disabled=False
    )

    title = widgets.Text(
        value='Population Density',
        description='Title',
        disabled=False
    )
    size_widget = widgets.interact(change_size,x=size);
    #size_widget.children[0].description = 'Plot Size'
    #size_widget.children[0].value = 600
    title_widget = widgets.interact(change_title,x=title);


    colors = list(RdYlGn[8]) 
    colors.reverse()
    #Is there a better color mapper?
    mapper = LinearColorMapper(palette=colors, low=viz_table.Population.min(),
                               high=viz_table.Population.max())

    color_bar = ColorBar(color_mapper=mapper, location=(0, 0),
                     ticker=BasicTicker(desired_num_ticks=len(colors)))

    
    # Create the default figure
    fig = []  # Storing the figure in a singular list is a bit of a 
              # hack. We need it to properly mutate the current
              # figure in our callbacks.
    p = heatmap(600,'Population Density')
    fig.append(p)
    with output_figure:
        show(fig[0])

    




    #heatmap_out = interactive(heatmap, size = size, title=title)
    #heatmap_out = widgets.interactive_output(heatmap, size = size, title=title)
    #heatmap_out = widgets.interactive_output(heatmap,{size: 'size', title:'title'})
    data_store.close()
    
    # This creates the menu 
    menu=widgets.VBox([size,
                       title])


    """Create the full app with menu and output"""
    # The Layout adds some styling to our app.
    # You can add Layout to any widget.
    app_layout = widgets.Layout(display='flex',
                    flex_flow='row nowrap',
                    align_items='center',
                    border='none',
                    width='100%',
                    margin='5px 5px 5px 5px')

    # The final app is just a box
    app=widgets.Box([output_figure], layout=app_layout)

    # Display the app
    display(app)

And with that being so weird to get working, I wonder if you original code works if you go through all that.


Related to it working if you go run the code in JupyterLab and then back to the classic interface, I wonder if what @psuchemedia (Tony Hirst) notes in his blog post has anything to do with it:

“One of the blockers I’ve had to date running … in JupyterLab was the requirement to have just a single instance of the widget running in a notebook. This is an artefact (I think) of the jp_proxy_widget having a hardwired HTML element reference that needs to be unique.”

1 Like

@fomightez Thanks, I am seeing that same dynamic. Interestingly Bokeh has a tutorial that uses binder and has an interactive display but I still can’t seem to make the conversion

I also posted in Bokeh discourse to see if they have some insights.

1 Like

Did you find a particular tutorial there that is close and works at present? If you point me to it, I’d be happy to take a look.

My concern is that ipywidgets has changed significantly in the past couple of years and most of the tutorials I see there were done two years ago with some typos fixed about year or 9 months ago.

No not yet, I have been looking at the source code without much luck

output_notebook

The map is occurring I can see it if I use output_file instead but when I look at the webpages html code it says hidden, but I can’t figure out how to change it.

Not too familiar with this but some findings-

It appears to be originating in _initialize_references_json when handing the JSON data:

{
    "attributes": {
        "axis": {
            "id": "1417"
        },
        "coordinates": null,
        "dimension": 1,
        "group": null,
        "ticker": null
    },
    "id": "1424",
    "type": "Grid"
},

It’s specifically erroring because the property coordinates cannot be found.

On the same binder posted above I’m also seeing a warning that the JS is using version 2.3.0 while Python is 2.4.0. Perhaps coordinates is a new property and the JS needs to be upgraded?

1 Like

@blois Thanks, that was the same conclusion as Bokeh. I will work to resolve.

@blois and @fomightez issue is fixed, error was being caused by using Bokeh 2.4 and resolved if I specified bokeh == 2.3 in the requirements.

In addition, if I upgraded to bokeh 2.4 on my local machine it also did not display.

Thanks for the help.

1 Like