diff --git a/DashboardApp_WebVersion.png b/DashboardApp_WebVersion.png
index 43c52af..809941c 100644
Binary files a/DashboardApp_WebVersion.png and b/DashboardApp_WebVersion.png differ
diff --git a/jogging_dashboard_browser_app.py b/jogging_dashboard_browser_app.py
index e7a0180..9584792 100644
--- a/jogging_dashboard_browser_app.py
+++ b/jogging_dashboard_browser_app.py
@@ -781,7 +781,7 @@ def create_map_plot(df):
hovertemplate=(
"Time: %{customdata[5]}
" +
"Distance: %{customdata[0]:.2f} km
" +
- "Elevation: %{customdata[1]:.0f} m ü.NN (%{customdata[2]:+.0f} m zum Start)
" + #„m ü.NN" bedeutet Meter über Normal-Null
+ "Elevation: %{customdata[1]:.0f} m ASL (Δ %{customdata[2]:+.0f} m from start)
" + #"ASL" = above sea level; diff. vs start value"
"Speed: %{customdata[3]:.1f} km/h
" +
"Heart Rate: %{customdata[4]:.0f} bpm"
),
@@ -1160,7 +1160,7 @@ def create_pixel_heatmap(dataframes,
img_b64 = _fig_to_base64(fig_mpl)
plotly_title = (
- f'Pixel-Heatmap · {city_code} · {n_city_runs} Läufe (Region)'
+ f'Pixel-Heatmap · {city_code} · {n_city_runs} runs (region)'
if mode == 'city' and city_code else 'Pixel-Heatmap · Einzellauf'
)
fig = go.Figure()
@@ -1438,7 +1438,7 @@ def create_pixel_pace_map(df, img_width=900, img_height=900,
sec_v = int((valid_mean - min_v) * 60)
fig_mpl.text(0.5, 0.97,
f'Pace-Map · Ø {min_v}:{sec_v:02d} min/km | '
- f'schnell: {p5:.1f} langsam: {p95:.1f} min/km',
+ f'Slow: {p95:.1f} Fast: {p5:.1f} min/km',
color='white', fontsize=10, ha='center', va='top',
transform=fig_mpl.transFigure)
@@ -1735,7 +1735,8 @@ def create_elevation_plot(df, smooth_points=500):
y=y_smooth,
mode='lines',
line=dict(color='#2b5290', width=2), # Weiße Linie für bessere Sichtbarkeit
- name='Elevation',
+ hovertemplate='Time: %{x|%H:%M:%S}
Δ Elevation = %{y:.1f} m',
+ #name='Elevation',
showlegend=False
))
@@ -1752,9 +1753,9 @@ def create_elevation_plot(df, smooth_points=500):
# Layout im Dark Theme
fig.update_layout(
- title=dict(text='Höhenprofil (relativ zum Ausgangswert: 0m)', font=dict(size=16, color='white')),
- xaxis_title='Zeit',
- yaxis_title='Höhe relativ zum Start (m)',
+ title=dict(text='Elevation Profile (relative to start: 0 m)', font=dict(size=16, color='white')),
+ xaxis_title='Time',
+ yaxis_title='Relative Elevation (m)',
template='plotly_dark',
paper_bgcolor='#1e1e1e',
plot_bgcolor='#111111',
@@ -1852,51 +1853,6 @@ def create_elevation_plot(df, smooth_points=500):
-# -----------------------------------------------------------------------------
-# DEVIATION-PLOT: Distanz-Zeit-Diagramm
-# -----------------------------------------------------------------------------
-def create_deviation_plot(df):
- # Compute mean velocity in km/s
- vel_kmps_mean = df['cum_dist_km'].iloc[-1] / df['time_diff_sec'].iloc[-1]
- # Expected cumulative distance assuming constant mean velocity
- df['cum_dist_km_qmean'] = df['time_diff_sec'] * vel_kmps_mean
- # Deviation from mean velocity distance
- df['del_dist_km_qmean'] = df['cum_dist_km'] - df['cum_dist_km_qmean']
- # Plot the deviation
- fig = px.line(
- df,
- x='time_loc',
- y='del_dist_km_qmean',
- labels={
- 'time_loc': 'Zeit',
- 'del_dist_km_qmean': 'Δ Strecke (km)'
- },
- template='plotly_dark',
- )
- fig.update_layout(
- title=dict(text='Abweichung von integriertem Durchschnittstempo', font=dict(size=16)),
- yaxis_title='Abweichung (km)',
- xaxis_title='Zeit',
- height=400,
- paper_bgcolor='#1e1e1e',
- plot_bgcolor='#111111',
- font=dict(color='white', size=14),
- margin=dict(l=40, r=40, t=50, b=40),
- uirevision='constant', # Avoiding not needed Re-renderings
- )
- # Add horizontal reference line at y=0
- fig.add_shape(
- type='line',
- x0=df['time_loc'].iloc[0],
- x1=df['time_loc'].iloc[-1],
- y0=0,
- y1=0,
- line=dict(color='gray', width=1, dash='dash'),
- name='Durchschnittstempo'
- )
- return fig
-
-
# -----------------------------------------------------------------------------
# SPEED-PLOT:
# -----------------------------------------------------------------------------
@@ -1909,12 +1865,13 @@ def create_speed_plot(df):
y=df['speed_kmh_smooth'][~mask],
mode='lines',
name='Geglättete Geschwindigkeit',
- line=dict(color='royalblue')
+ line=dict(color='royalblue'),
+ hovertemplate='Time: %{x|%H:%M:%S}
Speed = %{y:.1f} km/h',
))
fig.update_layout(
- title=dict(text=f'Tempo über die Zeit (geglättet) - Ø {mean_speed_kmh:.2f} km/h', font=dict(size=16)),
- xaxis=dict(title='Zeit', tickformat='%H:%M', type='date'),
- yaxis=dict(title='Geschwindigkeit (km/h)', rangemode='tozero'),
+ title=dict(text=f'Speed over Time (smoothed) - Ø {mean_speed_kmh:.2f} km/h', font=dict(size=16)),
+ xaxis=dict(title='Time', tickformat='%H:%M', type='date'),
+ yaxis=dict(title='Speed (km/h)', rangemode='tozero'),
template='plotly_dark',
paper_bgcolor='#1e1e1e',
plot_bgcolor='#111111',
@@ -1936,6 +1893,51 @@ def create_speed_plot(df):
+# -----------------------------------------------------------------------------
+# DEVIATION-PLOT: Distanz-Zeit-Diagramm
+# -----------------------------------------------------------------------------
+def create_deviation_plot(df):
+ # Compute mean velocity in km/s
+ vel_kmps_mean = df['cum_dist_km'].iloc[-1] / df['time_diff_sec'].iloc[-1]
+ # Expected cumulative distance assuming constant mean velocity
+ df['cum_dist_km_qmean'] = df['time_diff_sec'] * vel_kmps_mean
+ # Deviation from mean velocity distance
+ df['del_dist_km_qmean'] = df['cum_dist_km'] - df['cum_dist_km_qmean']
+ # Plot the deviation
+ fig = go.Figure()
+ fig.add_trace(go.Scatter(
+ x=df['time_loc'],
+ y=df['del_dist_km_qmean'],
+ mode='lines',
+ name='Δ Strecke (km)',
+ line=dict(color='royalblue'),
+ hovertemplate='Time: %{x|%H:%M:%S}
Δ Strecke = %{y:.2f} km',
+ ))
+ fig.update_layout(
+ title=dict(text='Distance deviation from integrated average pace', font=dict(size=16)),
+ yaxis_title='Δ Distance (km)',
+ xaxis_title='Time',
+ template='plotly_dark',
+ height=400,
+ paper_bgcolor='#1e1e1e',
+ plot_bgcolor='#111111',
+ font=dict(color='white', size=14),
+ margin=dict(l=40, r=40, t=50, b=40),
+ uirevision='constant', # Avoiding not needed Re-renderings
+ )
+ # Add horizontal reference line at y=0
+ fig.add_shape(
+ type='line',
+ x0=df['time_loc'].iloc[0],
+ x1=df['time_loc'].iloc[-1],
+ y0=0,
+ y1=0,
+ line=dict(color='gray', width=1, dash='dash'),
+ name='Durchschnittstempo'
+ )
+ return fig
+
+
# -----------------------------------------------------------------------------
# HEART-RATE-PLOT:
@@ -1963,11 +1965,11 @@ def create_heart_rate_plot(df):
x=df['time'][~mask],
y=df['heart_rate'][~mask],
mode='lines',
- line=dict(color='#ff2c48', width=1.5), # etwas dünner für gezackte Linie
+ line=dict(color='#ff1d34', width=1.5), # etwas dünner für gezackte Linie
showlegend=False,
hovertemplate=(
- "Zeit: %{x}
" +
- "Herzfrequenz: %{y:.0f} bpm
" +
+ "Time: %{x}
" +
+ "Heart Rate: %{y:.0f} bpm
" +
""
)
))
@@ -2061,19 +2063,19 @@ def create_heart_rate_plot(df):
)
# Layout
- title_text = f'Herzfrequenz über die Zeit (geglättete)'
+ title_text = f'Heart Rate over Time:'
if mean_hr > 0:
- title_text += f' - Ø {mean_hr:.0f} bpm (Range: {min_hr:.0f}-{max_hr:.0f})'
+ title_text += f' Ø {mean_hr:.0f} bpm (Range: {min_hr:.0f}-{max_hr:.0f})'
fig.update_layout(
title=dict(text=title_text, font=dict(size=16, color='white')),
xaxis=dict(
- title='Zeit',
+ title='Time',
tickformat='%H:%M',
type='date'
),
yaxis=dict(
- title='Herzfrequenz (bpm)',
+ title='Heart Rate (bpm)',
range=[70, 200] # instead of: rangemode='tozero'
),
template='plotly_dark',
@@ -2313,7 +2315,7 @@ def create_pace_bars_plot(df, formatted_pace=None):
fig.update_layout(
title=dict(
- text=f'Tempo je Kilometer · Ø {formatted_pace}',
+ text=f'Pace per km · Ø {formatted_pace}',
font=dict(size=15, color='white')
),
xaxis=dict(
@@ -2328,7 +2330,7 @@ def create_pace_bars_plot(df, formatted_pace=None):
color='white',
),
yaxis=dict(
- title='',
+ title='Distance (km)',
autorange='reversed', # km 1 oben, letzter km unten (wie Strava)
tickmode='array',
tickvals=list(range(len(y_labels))), # ← 0,1,2...
@@ -2369,7 +2371,7 @@ app.layout = html.Div([
html.Div([
# Linke Seite: Datei-Dropdown
html.Div([
- html.Label("Datei wählen:", style={'color': '#aaaaaa', 'marginBottom': '5px'}),
+ html.Label("Select File:", style={'color': '#aaaaaa', 'marginBottom': '5px'}),
dcc.Dropdown(
id='file-dropdown',
options=list_files(),
@@ -2429,8 +2431,8 @@ app.layout = html.Div([
'color': '#aaaaaa', 'margin': '10px 0 0 0', 'fontSize': '16px'
}),
html.P(
- "Plot 1) Heatmap: Einzellauf oder alle Läufe der Region umschalten.\n"
- "Plot 2), 3) & 4) Elevation-, Pace- & HR-Map: des aktuell gewählten Laufs.",
+ "Plot 1) Heatmap: Toggle between single run or all runs in the region.\n"
+ "Plot 2), 3) & 4) Elevation, Pace & HR maps: of the currently selected run.",
style={'color': '#aaaaaa', 'margin': '2px 0 8px 0', 'fontSize': '12px'}
),
], style={'padding': '0 20px'}),
@@ -2451,7 +2453,7 @@ app.layout = html.Div([
# dcc.Checklist als Toggle-Switch (ein Checkbox = ON/OFF)
dcc.Checklist(
id='heatmap-mode-toggle',
- options=[{'label': ' Alle Läufe (Region)', 'value': 'city'}],
+ options=[{'label': ' All runs (regional)', 'value': 'city'}],
#value=[], # Standard: leer = Einzellauf
value=['city'], # Standard: Region aktiv - Jezt ist immer der Harken gesetzt!
inputStyle={
@@ -2507,8 +2509,8 @@ app.layout = html.Div([
# ENDE !!!!!!!!!!!!!!!!
dcc.Graph(id='fig-elevation'),
- dcc.Graph(id='fig_deviation'),
dcc.Graph(id='fig_speed'),
+ dcc.Graph(id='fig_deviation'),
dcc.Graph(id='fig_hr'),
dcc.Graph(id='fig_pace_bars')
])
@@ -2531,8 +2533,8 @@ def load_data(selected_file): # Dateipfad der ausgewählten Da
Output('info-banner', 'children'),
Output('fig-map', 'figure', allow_duplicate=True),
Output('fig-elevation', 'figure'),
- Output('fig_deviation', 'figure'),
Output('fig_speed', 'figure'),
+ Output('fig_deviation', 'figure'),
Output('fig_hr', 'figure'),
Output('fig_pace_bars', 'figure'),
# NEU: drei Pixel-Maps
@@ -2550,8 +2552,8 @@ def update_all_plots(json_data):
info = create_info_banner(df)
fig_map = create_map_plot(df)
fig_elev = create_elevation_plot(df)
- fig_dev = create_deviation_plot(df)
fig_speed = create_speed_plot(df)
+ fig_dev = create_deviation_plot(df)
fig_hr = create_heart_rate_plot(df)
fig_pace = create_pace_bars_plot(df)
@@ -2560,7 +2562,7 @@ def update_all_plots(json_data):
fig_pixel_pace = create_pixel_pace_map(df, img_width=800, img_height=500)
fig_pixel_hr = create_pixel_hr_map(df, img_width=800, img_height=500) # ← NEU
- return (info, fig_map, fig_elev, fig_dev, fig_speed, fig_hr, fig_pace,
+ return (info, fig_map, fig_elev, fig_speed, fig_dev, fig_hr, fig_pace,
fig_pixel_elevation, fig_pixel_pace, fig_pixel_hr)
@@ -2621,7 +2623,7 @@ def update_pixel_heatmap(selected_file, toggle_value):
n_city_runs=len(city_dfs),
img_width=800, img_height=500, # ← NEU
)
- city_info_text = f'Region {city_code} · Einzellauf · Count aus {len(city_dfs)} Läufen'
+ city_info_text = f'Region {city_code} · single run · Count of {len(city_dfs)} runs'
else:
# --- Region-Modus: alle Läufe anzeigen ---
fig = create_pixel_heatmap(
@@ -2631,7 +2633,7 @@ def update_pixel_heatmap(selected_file, toggle_value):
n_city_runs=len(city_dfs),
img_width=800, img_height=500, # ← NEU
)
- city_info_text = f'Region {city_code} · {len(city_dfs)} Läufe geladen'
+ city_info_text = f'Region {city_code} · {len(city_dfs)} runs loaded'
return fig, city_info_text