Got it to work
I can plot and overlay a scatter3d and surface plot on top of one another using two different data sets.
I can also update each data set with new data for both plot types individually, and clear both individually.
Example in case anyone else needs to know how to update and clear 3D plots in a similar method.
import dash
from dash.dependencies import Output, Input, State
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
from dash.exceptions import PreventUpdate
import numpy as np
import pandas as pd
# Read data from a csv
z_surf = 50*pd.read_csv(
'https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv')
# Create second surface data set
z_surf_neg = -1*z_surf
# Create scatter3D points
x = np.linspace(0, 20, 70)
X, Y = np.meshgrid(x, x)
z_scat = X*Y**2-X**2 + Y**3
# Create second scatter3D data set
z_scat_neg = -1*z_scat
layout = go.Layout(width = 700, height =700,
title_text='Test example')
fig = go.Figure()
# Predefined empty surface and scatter3D plots
fig.add_surface(name='surf_p', z=[])
fig.add_scatter3d(name='scat_p', x=[], y=[], z=[], mode='markers',
marker=dict(size=1, color=X.flatten(),colorscale='Reds'))
# Predefine surface and scatter3D plots with data
# fig.add_surface(name='surf_p', z=z_surf)
# fig.add_scatter3d(name='scat_p', x=X.flatten(), y=Y.flatten(), z=z_scat.flatten(), mode='markers',
# marker=dict(size=1, color=X.flatten(),colorscale='Reds'))
app = dash.Dash(__name__)
app.layout = html.Div(
[
# Plot figure
dcc.Graph(id='graph', figure=fig),
# Buttons to update graph
html.Button(id='surf-button', n_clicks=0, children='Surf Pos'),
html.Button(id='scat3D-button', n_clicks=0, children='Scatter3D Pos'),
html.Button(id='surf-button-neg', n_clicks=0, children='Surf Neg'),
html.Button(id='scat3D-button-neg', n_clicks=0, children='Scatter3D Neg'),
html.Button(id='surf-button-clear', n_clicks=0, children='Surf Clear'),
html.Button(id='scat3D-button-clear', n_clicks=0, children='Scatter3D Clear')
]
)
@app.callback(Output('graph', 'figure'),
[Input('surf-button', 'n_clicks'),
Input('scat3D-button', 'n_clicks'),
Input('surf-button-neg', 'n_clicks'),
Input('scat3D-button-neg', 'n_clicks'),
Input('surf-button-clear', 'n_clicks'),
Input('scat3D-button-clear', 'n_clicks')],
[State('graph', 'figure')])
def data_headers(surfPos, scat3DPos, surfNeg, scat3DNeg, surfClear, scat3DClear, figure):
ctx = dash.callback_context
if not ctx.triggered:
print("Not triggered")
raise PreventUpdate()
else:
button_id = ctx.triggered[0]['prop_id'].split('.')[0]
# How to update figure plot data:
# figure['data'][0] references the surface plot
# Update figure['data'][0]['z'] to change surface data
# figure['data'][1] references the scatter3D plot
# Update figure['data'][1]['x']
# Update figure['data'][1]['y']
# Update figure['data'][1]['z']
if button_id == "surf-button":
print("Surf positive updated.")
figure['data'][0]['z'] = z_surf.to_numpy()
elif button_id == "scat3D-button":
print("scatter3D positive updated.")
figure['data'][1]['x'] = X.flatten()
figure['data'][1]['y'] = Y.flatten()
figure['data'][1]['z'] = z_scat.flatten()
elif button_id == "surf-button-neg":
print("Surf negative updated.")
figure['data'][0]['z'] = z_surf_neg.to_numpy()
elif button_id == "scat3D-button-neg":
print("Scatter3D negative updated.")
figure['data'][1]['x'] = X.flatten()
figure['data'][1]['y'] = Y.flatten()
figure['data'][1]['z'] = z_scat_neg.flatten()
elif button_id == "surf-button-clear":
print("Surf clear.")
z_clear = []
figure['data'][0]['z'] = z_clear
elif button_id == "scat3D-button-clear":
print("Scatter3D clear.")
Scatter3DClear = []
figure['data'][1]['x'] = Scatter3DClear
figure['data'][1]['y'] = Scatter3DClear
figure['data'][1]['z'] = Scatter3DClear
return figure
if __name__ == '__main__':
app.run_server(debug=True)
Now all I need to do is work out how to handle multiple scatter3D plots? The scatter3D data will be dynamic. So I need a way of generating more than 1 scatter3D plot dynamically?