Help getting glyph patches to update from changing GeoJSONSourceData

Hello all,

I’m plotting patches from a GeoJSONDataSource source and I would like to update what I am displaying based on a Select widget. It seems like I am able to update and change the data source but it is not displaying in the patches plot (patches plot still shows the original GeoJSONDataSource even though I have updated it to only be a subset of the data).
The following code is a simplified version of what I’m trying to do: I have created 4 shapes in geoJSON data format, have created a GeoJSONDataSource object from this, and have a a Select Widget which allows me to chose between the 4 different shapes. If any one of those shapes is selected in the Select Widget, I would like to only show that shape being plotted.

import pandas as pd
import geopandas as gpd
import numpy as np
import json
from bokeh import events
from bokeh.models import (Select, Column, Row, ColumnDataSource, HoverTool, 
                      GeoJSONDataSource)
from bokeh.plotting import figure
from bokeh.io import curdoc
def make_geo_plot(src):
    # function to create the spatial plot with polygons
    p = figure(width=300, height=300, title="Select area", tools=['tap', 'pan', 'box_zoom', 'wheel_zoom','reset'])

    layer = p.patches('xs', 'ys', fill_alpha=0.2, fill_color='black',
          line_color='black', line_width=0.5, source=src)
          
    return p, layer


def update_goe_plot_from_select(attrname, old, new):
    # Callback for the dropdown menu which updates the geo chart
    new_geo_src_data = get_source_data(geojson_pd, area_select.value)   
    new_geo_src_stack = new_geo_src_data.stack()
    geo_source.geojson = new_geo_src_stack.to_json(default_handler=str)
    layer.update(data_source=geo_source)

def get_source_data(df, fieldid):
    # function to get a subset of the multi-hierarchical DataFrame

    geojson_pd_filter = df[df.key==fieldid]
    return geojson_pd_filter

#example polygons
geojson = """{"type":"FeatureCollection",
"crs":{"type":"name","properties":{"name":"urn:ogc:def:crs:OGC:1.3:CRS84"}},
"features":[
{"type":"Feature","properties":{"key":"area_a"},"geometry":{"type":"MultiPolygon","coordinates": 
[[[[-108.8,42.7],[-104.5,42.0],[-108.3,39.3],[-108.8,42.7]]]]}},
{"type":"Feature","properties":{"key":"area_b"},"geometry":{"type":"MultiPolygon","coordinates": 
[[[[-106.3,44.0],[-106.2,42.6],[-103.3,42.6],[-103.4,44.0],[-106.3,44.0]]]]}},
{"type":"Feature","properties":{"key":"area_d"},"geometry":{"type":"MultiPolygon","coordinates": 
[[[[-104.3,41.0],[-101.5,41.0],[-102.9,37.8],[-104.3,41.0]]]]}},
{"type":"Feature","properties":{"key":"area_c"},"geometry":{"type":"MultiPolygon","coordinates": 
[[[[-105.8,40.3],[-108.3,37.7],[-104.0,37.4],[-105.8,40.3]]]]}}]}"""

#following to act as dataframe for easy filtering
geojson_json = json.loads(geojson)
geojson_pd = gpd.GeoDataFrame.from_features(geojson_json["features"])

#crearting the geo source for plotting the shapes as patches:
geo_source = GeoJSONDataSource(geojson=geojson)

#creating the select widget:
area_ids = ['area_a', 'area_b', 'area_c', 'area_d']
area_select = Select(value=area_ids[0], title='Select area', options=area_ids)
area_select.on_change('value', update_goe_plot_from_select)

p_geo, layer = make_geo_plot(geo_source)

# add to document
curdoc().add_root(Row(Column(area_select), p_geo))

I am new to Bokeh and I’d be greatly appreciative if someone could send me in the right direction to solve this. thank you so much.

The line layer.update(data_source=geo_source) is unnecessary - just changing the geojson attribute of geo_source should be enough.

It’s not working in your case because new_geo_src_stack.to_json(default_handler=str) does not return the data you expect. If you try changing the value of the Select widget and check the JS console, you’ll see

Uncaught Error: Bokeh only supports type GeometryCollection and FeatureCollection at top level

That’s because that call to to_json returns something like

{"(3, 'geometry')":"MULTIPOLYGON (((-105.8 40.3, -108.3 37.7, -104 37.4, -105.8 40.3)))","(3, 'key')":"area_c"}

Something that Bokeh cannot understand at all.

It seems to work if you replace the callback with this one:

def update_goe_plot_from_select(attrname, old, new):
    new_geo_src_data = get_source_data(geojson_pd, area_select.value)
    geo_source.geojson = json.dumps(new_geo_src_data.__geo_interface__)

thank you so much! this is exactly what I needed.