Change in TextInput data doesn't update the plot

Hello all,

I’m trying to view coordinates based on the their distance from an origin defined through two TextInput widgets. However, when I change the values the view does not change.

sample code is below:

import numpy as np
import pandas as pd
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource, CustomJS, TextInput, CustomJSFilter,CDSView
import math
from bokeh.layouts import column, row
from bokeh.io import show

df = pd.DataFrame(np.random.rand(100,2),columns=list('Xy'))

def distance(origin, destination):
   
    lat1, lon1 = origin
    lat2, lon2 = destination
    radius = 6371  # km

    dlat = math.radians(lat2 - lat1)
    dlon = math.radians(lon2 - lon1)
    a = (math.sin(dlat / 2) * math.sin(dlat / 2) +
         math.cos(math.radians(lat1)) * math.cos(math.radians(lat2)) *
         math.sin(dlon / 2) * math.sin(dlon / 2))
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
    d = radius * c

    return d


cord_1, cord_2 = 0.5,0.8

df['distance'] = [distance((cord_1,cord_2),(df['X'][i],df['y'][i])) for i in range(0,len(df['X']))]
source=ColumnDataSource(df)
rendered_source=ColumnDataSource(df)



cord_x = TextInput(title="X-Coordinates", value='0.5')
cord_y = TextInput(title="Y-Coordinates", value='0.8')


TOOLTIPS = [

    ('Distance','@distance')
        ]





callback = CustomJSFilter(args=dict(cord_x=cord_x, cord_y=cord_y),
                    code="""
function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) {
  var R = 6371; // Radius of the earth in km
  var dLat = deg2rad(lat2-lat1);  // deg2rad below
  var dLon = deg2rad(lon2-lon1); 
  var a = 
    Math.sin(dLat/2) * Math.sin(dLat/2) +
    Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * 
    Math.sin(dLon/2) * Math.sin(dLon/2)
    ; 
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
  var d = R * c; // Distance in km
  return d;
}

function deg2rad(deg) {
  return deg * (Math.PI/180)
}
                     
var A = cord_x.value;
var B = cord_y.value;
var indices = [];
    
for(var i = 0; i < source.data['X'].length ; i++) {
    if(getDistanceFromLatLonInKm(A,B,source.data['X'][i],source.data['y'][i]) <= 50){
         indices.push(true);
      } else {
         indices.push(false);
      }
   }
   return indices;
""")

view=CDSView(source=source,filters=[callback])


p = figure(title='Sample Distance',width = 800, height = 600,tooltips = TOOLTIPS)
p.circle(x='X',y='y',size = 10,source=source,view=view)


layout = row(
    p,
    column(cord_x, cord_y),
)

show(layout)

Thx

Hey @Al7 , thanks for the great MRE, this was super easy to plug and play :slight_smile:

What you’re missing is adding “listener” callbacks to cord_x and cord_y. Basically you need to attach another “mini” callback to emit the source’s change whenever the ‘value’ property of cord_x/cord_y change. Emitting the source’s change will trigger your CustomJSFilter to “re-execute” its filtering action.

So near the end of your script (like after you make the view), just put:

cord_x.js_on_change('value',CustomJS(args=dict(source=source)
                                     ,code='''
                                     source.change.emit()
                                     '''))
cord_y.js_on_change('value',CustomJS(args=dict(source=source)
                                     ,code='''
                                     source.change.emit()
                                     '''))
2 Likes

Thank you heaps @gmerritt123, works perfectly.