I want to link a plot containing patches (from a GeoJSONDataSource
) with a line chart but i'm having trouble getting the attributes of the selected patch.
Its basically a plot showing polygons, and when a polygon is selected, i want to update the line chart with a timeseries of data for that polygon. The line chart is driven by a normal ColumnDataSource
.
I can get the indices of the selected patch by adding a callback combined with geo_source.selected['1d']['indices']
. But how do i get the data/attributes which correspond to that index? I need to get a 'key' in the attributes which i can then use to update the line chart.
The GeoJSONDataSource
has no data
attribute in which i can lookup the data itself. Bokeh can use the attributes for things like coloring/tooltips etc, so i assume there must be a way to get these out of the GeoJSONDataSource
, i cant find it unfortunately.
edit:
Here is working toy example showing what i've got so far.
import pandas as pd
import numpy as np
from bokeh import events
from bokeh.models import (Select, Column, Row, ColumnDataSource, HoverTool,
Range1d, LinearAxis, GeoJSONDataSource)
from bokeh.plotting import figure
from bokeh.io import curdoc
import os
import datetime
from collections import OrderedDict
def make_plot(src):
# function to create the line chart
p = figure(width=500, height=200, x_axis_type='datetime', title='Some parameter',
tools=['xwheel_zoom', 'xpan'], logo=None, toolbar_location='below', toolbar_sticky=False)
p.circle('index', 'var1', color='black', fill_alpha=0.2, size=10, source=src)
return p
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'], logo=None)
p.patches('xs', 'ys', fill_alpha=0.2, fill_color='black',
line_color='black', line_width=0.5, source=src)
p.on_event(events.SelectionGeometry, update_plot_from_geo)
return p
def update_plot_from_geo(event):
# update the line chart based on the selected polygon
selected = geo_source.selected['1d']['indices']
if (len(selected) > 0):
first = selected[0]
print(geo_source.selected['1d']['indices'])
def update_plot(attrname, old, new):
# Callback for the dropdown menu which updates the line chart
new_src = get_source(df, area_select.value)
src.data.update(new_src.data)
def get_source(df, fieldid):
# function to get a subset of the multi-hierarchical DataFrame
# slice 'out' the selected area
dfsub = df.xs(fieldid, axis=1, level=0)
src = ColumnDataSource(dfsub)
return src
# example timeseries
n_points = 100
df = pd.DataFrame({('area_a','var1'): np.sin(np.linspace(0,5,n_points)) + np.random.rand(100)*0.1,
('area_b','var1'): np.sin(np.linspace(0,2,n_points)) + np.random.rand(100)*0.1,
('area_c','var1'): np.sin(np.linspace(0,3,n_points)) + np.random.rand(100)*0.1,
('area_d','var1'): np.sin(np.linspace(0,4,n_points)) + np.random.rand(100)*0.1},
index=pd.DatetimeIndex(start='2017-01-01', freq='D', periods=100))
# 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]]]]}}
]
}"""
geo_source = GeoJSONDataSource(geojson=geojson)
# populate a drop down menu with the area's
area_ids = sorted(df.columns.get_level_values(0).unique().values.tolist())
area_ids = [str(x) for x in area_ids]
area_select = Select(value=area_ids[0], title='Select area', options=area_ids)
area_select.on_change('value', update_plot)
src = get_source(df, area_select.value)
p = make_plot(src)
pgeo = make_geo_plot(geo_source)
# add to document
curdoc().add_root(Row(Column(area_select, p), pgeo))
Save the code in a .py
file and load with bokeh serve example.py --show