Hi,
I have a Mapbox section with markers plotted on it. When I zoom on the markers after filtering the markers data with a slider, the map refreshes itself to its initial zoom and position.
Is it possible to save these 2 parameters after filtering ?
Thank you.
Good question. You can pass in the current figure
or relayoutData
as state
and use the range values from that figure in the new figure that you create. Here’s an example: https://plot.ly/dash/gallery/new-york-oil-and-gas/ (see the “Lock camera” checkbox) and here’s the relevant section in the code: https://github.com/plotly/dash-oil-and-gas-demo/blob/d937ebbbf9da244d9832e25c42b46319148d9ef5/app.py#L437-L448
Here’s a simple example that shows the two types of modes with a cartesian plot:
import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import copy
app = dash.Dash()
FIGURE = {
'data': [{
'x': [1, 2, 3],
'y': [4, 3, 6],
'mode': 'markers',
'marker': {
'size': 8
}
}],
'layout': {
'xaxis': {},
'yaxis': {},
}
}
app.layout = html.Div([
html.H3('Persistent Zoom on Updates'),
html.Div('''
Try zooming into the graph (clicking and dragging on the graph),
then updating the text box. Notice how the graph does not zoom out
when the graph is updated. Switching to "Refresh View" will
redraw the graph with auto-range enabled.
'''),
dcc.Input(id='my-input'),
dcc.RadioItems(
id='lock-zoom',
options=[{'label': i, 'value': i} for i in ['Lock View', 'Refresh View']],
value='Lock View'
),
dcc.Graph(
id='my-graph',
figure=FIGURE
)
])
@app.callback(
Output('my-graph', 'figure'),
[Input('my-input', 'value'),
Input('lock-zoom', 'value')],
[State('my-graph', 'relayoutData')])
def update_graph(value, lock_zoom, relayout_data):
new_figure = copy.deepcopy(FIGURE)
new_figure['layout']['title'] = value
# relayout_data contains data on the zoom and range actions
print(relayout_data)
if relayout_data and lock_zoom == 'Lock View':
if 'xaxis.range[0]' in relayout_data:
new_figure['layout']['xaxis']['range'] = [
relayout_data['xaxis.range[0]'],
relayout_data['xaxis.range[1]']
]
if 'yaxis.range[0]' in relayout_data:
new_figure['layout']['yaxis']['range'] = [
relayout_data['yaxis.range[0]'],
relayout_data['yaxis.range[1]']
]
return new_figure
app.css.append_css({"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"})
if __name__ == '__main__':
app.run_server(debug=True)
You’ll have to style the checkbox yourself. You could also remove the checkbox and have this as the default behaviour.
I tried to implement the relayoutdata feature in my code but I was unable to do the same. Can anyone help me regarding this?
#call back for All Plating Lines
@app.callback(
dash.dependencies.Output('table', 'children'),
[dash.dependencies.Input('lineSelect', 'value'),
dash.dependencies.Input('refresh-table', 'n_intervals'),])
def filter_table(input,num):
if num == 99999999999:
raise PreventUpdate
else:
print ("The code is inside the function")
df2 = pd.read_sql_query(query_3, engine)
df2['location'] = df2['location'].replace(regex='_I_Act',value='')
df2['line'] = np.where(df2.location.str[0] == 'A' ,df2.line+' A',df2.line)
df2['line'] = np.where(df2.location.str[0] == 'B' ,df2.line+' B',df2.line)
tableData = df2.pivot_table('alarmflag',['line','linespeed'],'location',aggfunc = 'first')
tableData = pd.DataFrame(tableData.to_records())
tableData.columns = [hdr.replace("('alarmflag',","").replace(")","") for hdr in tableData.columns]
td1 = tableData
td1['linespeed'] = td1['linespeed'].round(1)
td1['linespeed'] = td1['linespeed'].astype(str)
for j in td1.columns:
if j not in ['line','linespeed']:
td1[j] = j + td1[j]
sort0_cols = td1.columns[td1.columns.str.contains(pat="EC") ]
sort1_cols = td1.columns[td1.columns.str.contains(pat="Ni") ]
sort2_cols = td1.columns[td1.columns.str.contains(pat="Ag") ]
sort3_cols = td1.columns[td1.columns.str.contains(pat="Sn") ]
sort4_cols = td1.columns[(~td1.columns.str.contains(pat="EC") & ~td1.columns.str.contains(pat="Ni") &
~td1.columns.str.contains(pat="Ag") & ~td1.columns.str.contains(pat="Sn") &
~td1.columns.str.contains(pat="line") ) ]
td1 = pd.concat([td1['line'],td1['linespeed'],td1[sort0_cols],td1[sort1_cols],td1[sort2_cols],td1[sort3_cols],td1[sort4_cols]],axis=1)
td1['new'] = td1.apply(lambda x: ','.join(x.dropna()),axis=1)
td2 = td1['new'].apply(lambda x:pd.Series(x.split(',')))
td2.rename(columns = {0:'line'},inplace=True)
df = pd.DataFrame(data = td2)
dfFiltered = pd.DataFrame()
temp = input
dfFiltered = df[df.line.isin(temp)]
return html.Div([
dash_table.DataTable(
id='tab',
columns = [{"name": i, "id": i} for i in dfFiltered.columns],
data = dfFiltered.to_dict('records'),
style_cell={'maxWidth': 0,
'overflow': 'hidden',
'whiteSpace': 'normal',
'height': 'auto',
#'textOverflow': 'ellipsis',
'textAlign': 'center',
'font-family': 'Verdana',
'margin':'40px',
'font-weight': 'bold',
},
style_header = {'display': 'none'},
style_data_conditional = ([{'if':{'filter_query': '{{{col}}} is blank'.format(col=col),
'column_id' : col},
'backgroundColor': 'white',
'color': 'white' } for col in dfFiltered.columns] +
[{'if':{'filter_query': '{{{col}}} contains "."'.format(col=col),
'column_id' : col},
'backgroundColor': '#CC0000',
'color': 'white' } for col in dfFiltered.columns] +
[{'if':{'filter_query': '{{{col}}} contains "`"'.format(col=col),
'column_id' : col},
'backgroundColor': 'white',
'color': 'green' } for col in dfFiltered.columns] +
[{'if':{'filter_query': '{{{col}}} contains " "'.format(col=col),
'column_id' : col},
'backgroundColor': '#D3D3D3',
'color': 'black' } for col in dfFiltered.columns]
+ [{'if':{
'column_id' : 1},
'backgroundColor': '#D3D3D3',
'color': 'black' }]
),
)
, dcc.Graph(id="graph3", style={"width": "50%", "display": "inline-block"}),
dcc.Graph(id="graph4", style={"width": "50%", "display": "inline-block"}),])
#call back for selection on All Plating Lines
@app.callback(
[Output('graph3', 'figure'),Output('graph4', 'figure')],
[Input('graph3', 'relayoutData'),Input('graph4', 'relayoutData')],
[State('graph3','figure'), State('graph4','figure')])
def show_graph(relayoutData, *figures):
dff = pd.DataFrame(rows)
if selection is None:
n_intervals = 0
return {}, {}
else:
n_intervals = 99999999999
loc = dff.iloc[selection['row']][selection['column']-1]
loc = loc.replace('.','').replace('`','').replace(' ','').replace('_I_Act','')
input1 = loc + '_I_Act'
t1 = dff.iloc[selection['row']]['line']
input2 = t2 = t1[:-2]
t1 = 'CNMO_CDUP_' + t2
cnxn_1 = {AWS connection details}
cursor = cnxn_1.cursor()
query_4 = """Select COLUMN_NAME from information_schema.columns where TABLE_NAME = '{}'
and TABLE_SCHEMA = 'ppqa' and
(COLUMN_NAME like '{}%' or COLUMN_NAME = 'DateTime' )
order by ORDINAL_POSITION"""
table_columns = pd.read_sql_query(query_4.format(t1,loc), cnxn_1)
col_list = [i for i in table_columns['COLUMN_NAME']]
col_string = ', '.join([str(i) for i in col_list])
query_5 = """SELECT {}, '{}' as line, NOW() as lastrefreshdate
FROM ppqa.{}
where DateTime >= now() - interval 24 hour
order by DateTime"""
df = pd.read_sql_query(query_5.format(col_string,t2,t1), cnxn_1)
if len(df) == 0:
return {}, {}
else:
df['data point i'] = df.index + 1
filename = t2
volt_col = input1.replace('_I_Act','_U_Act')
tolerance_inputs = pd.read_excel("path"+input2 +".xlsx", sheet_name = 'Sheet1')
current_tolerance = int(tolerance_inputs["Current Tolerance for " + input1].loc[0])
voltage_tolerance = int(tolerance_inputs["Voltage Tolerance for Standard Deviation for " + volt_col].loc[0])
standard_dev_15_rows = df[df['data point i'] <= 15][volt_col].std()
df['rolling_average'] = df[volt_col].rolling(15).mean()
def func_voltage_mu_o(df):
if df['data point i'] <= 15:
return (df[volt_col])
else:
return (df['rolling_average'])
df['voltage_mu_o'] = df.apply(func_voltage_mu_o, axis = 1)
Voltage_SD = 0.045
df['voltage_sigma'] = Voltage_SD
output = []
for fig in figures:
fig=go.Figure()
fig.add_trace(go.Scatter(x=df['DateTime'],y=df[loc+'_I_Set'],name = 'Current Set Point',line=dict(color='black',width=4,dash='dash')))
fig.add_trace(go.Scatter(x=df['DateTime'],y=df[input1],name = 'Actual Current',line=dict(color='black',width=4)))
fig.add_trace(go.Scatter(x=df['DateTime'],y=df[loc+'_I_Set']+df[loc+'_I_Set']*current_tolerance/100,name = 'Upper Limit',line=dict(color='red',width=4)))
fig.add_trace(go.Scatter(x=df['DateTime'],y=df[loc+'_I_Set']-df[loc+'_I_Set']*current_tolerance/100,name = 'Lower Limit',line=dict(color='red',width=4)))
fig.update_layout(title= input2+' '+input1[:-6]+ ' : Current Vs Time',xaxis_title = 'Time',yaxis_title = 'Current (Amp)')
fig2=go.Figure()
fig2.add_trace(go.Scatter(x=df['DateTime'],y=df[loc+'_U_Set'],name = 'Actual Voltage',line=dict(color='black',width=4)))
fig2.add_trace(go.Scatter(x=df['DateTime'],y=df['voltage_mu_o'] + df['voltage_sigma'] * voltage_tolerance,name = 'Voltage Red UCL',line=dict(color='red',width=4)))
fig2.add_trace(go.Scatter(x=df['DateTime'],y=df['voltage_mu_o'] - df['voltage_sigma'] * voltage_tolerance,name = 'Voltage Red LCL',line=dict(color='red',width=4)))
fig2.update_layout(title= input2+' '+input1[:-6]+ ' : Voltage Vs Time',xaxis_title = 'Time',yaxis_title = 'Voltage (Volt)')
return fig,fig2
@app.callback(
Output('refresh-table','n_intervals'),
Input('tab', 'active_cell'))
def stop( selection):
if selection is None:
n_intervals = 0
return n_intervals
else:
n_intervals = 99999999999
return n_intervals
@chriddyp Any suggestions?