|
|
|
|
@ -1963,6 +1963,7 @@ def build_trajectories_tab(db_path: str, window_size: str) -> None: |
|
|
|
|
|
|
|
|
|
fig = go.Figure() |
|
|
|
|
trace_count = 0 |
|
|
|
|
helper_succeeded = False |
|
|
|
|
# New: delegate plotting selection to helper for testability |
|
|
|
|
# Note: select_trajectory_plot_data returns (fig, trace_count, banner_text) |
|
|
|
|
try: |
|
|
|
|
@ -1973,6 +1974,7 @@ def build_trajectories_tab(db_path: str, window_size: str) -> None: |
|
|
|
|
if fig2 is not None: |
|
|
|
|
fig = fig2 |
|
|
|
|
trace_count = trace_count2 |
|
|
|
|
helper_succeeded = True |
|
|
|
|
if banner_text: |
|
|
|
|
try: |
|
|
|
|
st.caption(banner_text) |
|
|
|
|
@ -2002,38 +2004,40 @@ def build_trajectories_tab(db_path: str, window_size: str) -> None: |
|
|
|
|
st.text_area("select_trajectory_plot_data traceback", tb, height=240) |
|
|
|
|
except Exception: |
|
|
|
|
pass |
|
|
|
|
for party in selected_parties: |
|
|
|
|
if party not in centroids: |
|
|
|
|
continue |
|
|
|
|
wids_sorted = sorted(centroids[party].keys()) |
|
|
|
|
xs_raw = [centroids[party][w][0] for w in wids_sorted] |
|
|
|
|
ys_raw = [centroids[party][w][1] for w in wids_sorted] |
|
|
|
|
xs = _ema_smooth(xs_raw, smooth_alpha) |
|
|
|
|
ys = _ema_smooth(ys_raw, smooth_alpha) |
|
|
|
|
# Preserve raw (unsmoothed) values per-point so hover can show both raw and smoothed |
|
|
|
|
custom_raw = [(float(rx), float(ry)) for rx, ry in zip(xs_raw, ys_raw)] |
|
|
|
|
colour = PARTY_COLOURS.get(party, "#9E9E9E") |
|
|
|
|
fig.add_trace( |
|
|
|
|
go.Scatter( |
|
|
|
|
x=xs, |
|
|
|
|
y=ys, |
|
|
|
|
mode="lines+markers", |
|
|
|
|
name=party, |
|
|
|
|
text=wids_sorted, # full window ID for hover |
|
|
|
|
customdata=custom_raw, |
|
|
|
|
line=dict(color=colour, shape="spline", smoothing=1.3), |
|
|
|
|
marker=dict(color=colour, size=8), |
|
|
|
|
hovertemplate=( |
|
|
|
|
f"<b>{party}</b><br>" |
|
|
|
|
"venster: %{text}<br>" |
|
|
|
|
"x (smoothed): %{x:.3f}<br>" |
|
|
|
|
"x (raw): %{customdata[0]:.3f}<br>" |
|
|
|
|
"y (smoothed): %{y:.3f}<br>" |
|
|
|
|
"y (raw): %{customdata[1]:.3f}<extra></extra>" |
|
|
|
|
), |
|
|
|
|
print(f"[TRAJ DEBUG] helper_succeeded={helper_succeeded}") |
|
|
|
|
if not helper_succeeded: |
|
|
|
|
for party in selected_parties: |
|
|
|
|
if party not in centroids: |
|
|
|
|
continue |
|
|
|
|
wids_sorted = sorted(centroids[party].keys()) |
|
|
|
|
xs_raw = [centroids[party][w][0] for w in wids_sorted] |
|
|
|
|
ys_raw = [centroids[party][w][1] for w in wids_sorted] |
|
|
|
|
xs = _ema_smooth(xs_raw, smooth_alpha) |
|
|
|
|
ys = _ema_smooth(ys_raw, smooth_alpha) |
|
|
|
|
# Preserve raw (unsmoothed) values per-point so hover can show both raw and smoothed |
|
|
|
|
custom_raw = [(float(rx), float(ry)) for rx, ry in zip(xs_raw, ys_raw)] |
|
|
|
|
colour = PARTY_COLOURS.get(party, "#9E9E9E") |
|
|
|
|
fig.add_trace( |
|
|
|
|
go.Scatter( |
|
|
|
|
x=xs, |
|
|
|
|
y=ys, |
|
|
|
|
mode="lines+markers", |
|
|
|
|
name=party, |
|
|
|
|
text=wids_sorted, # full window ID for hover |
|
|
|
|
customdata=custom_raw, |
|
|
|
|
line=dict(color=colour, shape="spline", smoothing=1.3), |
|
|
|
|
marker=dict(color=colour, size=8), |
|
|
|
|
hovertemplate=( |
|
|
|
|
f"<b>{party}</b><br>" |
|
|
|
|
"venster: %{text}<br>" |
|
|
|
|
"x (smoothed): %{x:.3f}<br>" |
|
|
|
|
"x (raw): %{customdata[0]:.3f}<br>" |
|
|
|
|
"y (smoothed): %{y:.3f}<br>" |
|
|
|
|
"y (raw): %{customdata[1]:.3f}<extra></extra>" |
|
|
|
|
), |
|
|
|
|
) |
|
|
|
|
) |
|
|
|
|
) |
|
|
|
|
trace_count += 1 |
|
|
|
|
trace_count += 1 |
|
|
|
|
|
|
|
|
|
# For trajectories, the chart spans multiple windows. Use the classifier's |
|
|
|
|
# per-window confidences aggregated (mean) to decide whether to use the |
|
|
|
|
|