Indeed, Patch() + annotation seem to work fine, also using go.Scattergl()
instead of go.Scatter()
should help if you have lot of points.
Here an example with 10,000 points:
import dash
from dash import dcc, html, Input, Output, Patch
import dash_bootstrap_components as dbc
import plotly.graph_objects as go
import numpy as np
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.SPACELAB])
N = 10000
r = np.random.uniform(0, 1, N)
theta = np.random.uniform(0, 2 * np.pi, N)
fig = go.Figure(data=go.Scattergl(
x=r * np.cos(theta),
y=r * np.sin(theta),
mode='markers',
marker=dict(
colorscale='Viridis',
line_width=1
),
))
fig.add_annotation()
fig.update_layout(height=1000)
app.layout = html.Div(
[
dbc.Input(id="input-id", type="number", min=0, max=N - 1, step=1, placeholder='Select ID',
className='text-center', style={'width': 150}),
dcc.Graph(id="graph-scatergl", figure=fig),
]
)
@app.callback(
Output("graph-scatergl", "figure"),
Input("input-id", "value"),
)
def highlight_point(point_id):
fig_patch = Patch()
if point_id is None:
fig_patch['data'][0]['marker']['color'] = r
fig_patch['data'][0]['marker']['opacity'] = 1
fig_patch['layout']['annotations'][0]['visible'] = False
return fig_patch
x = round(r[point_id] * np.cos(theta[point_id]), 2)
y = round(r[point_id] * np.sin(theta[point_id]), 2)
text = f'x = {x}<br>y = {y}'
marker_opacity = [0.2] * N
marker_opacity[point_id] = 1
fig_patch['data'][0]['marker']['opacity'] = marker_opacity
fig_patch['layout']['annotations'][0].update(
visible=True,
x=x, y=y,
text=text,
showarrow=False,
xshift=35,
bgcolor="rgba(255,255,255,0.8)",
)
return fig_patch
if __name__ == "__main__":
app.run_server(debug=True)
I made no comments in the code, don’t hesitate to ask me for details.
I used markers opacity to have a nice fading effect to have a better highlight on the selected point.
But if you have performance issues, you may remove opacity modification, as it updates an array of size N and maybe use an arrow with the annotation to point on the … point to be more precise.
And you can customize your annotation as you like, see Styling and Coloring Annotations