diff --git a/fit_app.py b/fit_app.py
index 96de463..8b23835 100644
--- a/fit_app.py
+++ b/fit_app.py
@@ -423,8 +423,7 @@ def create_map_plot(df):
-######################
-# NEUE VERSION:
+# #####################
def create_elevation_plot(df, smooth_points=500):
# Originale Daten
x = df['time']
@@ -433,23 +432,17 @@ def create_elevation_plot(df, smooth_points=500):
# Einfache Glättung: nur Y-Werte glätten, X-Werte beibehalten
if len(y) >= 4: # Genug Punkte für cubic interpolation
y_numeric = y.to_numpy()
-
# Nur gültige Y-Punkte für Interpolation
mask = ~np.isnan(y_numeric)
-
if np.sum(mask) >= 4: # Genug gültige Punkte
# Index-basierte Interpolation für Y-Werte
valid_indices = np.where(mask)[0]
valid_y = y_numeric[mask]
-
# Interpolation über die Indizes
- f = interp1d(valid_indices, valid_y, kind='cubic',
- bounds_error=False, fill_value='extrapolate')
-
+ f = interp1d(valid_indices, valid_y, kind='cubic', bounds_error=False, fill_value='extrapolate')
# Neue Y-Werte für alle ursprünglichen X-Positionen
all_indices = np.arange(len(y))
y_smooth = f(all_indices)
-
# Originale X-Werte beibehalten
x_smooth = x
else:
@@ -461,40 +454,96 @@ def create_elevation_plot(df, smooth_points=500):
fig = go.Figure()
- # Fläche unter der Kurve (mit geglätteten Daten)
- fig.add_trace(go.Scatter(
- x=x_smooth, y=y_smooth,
- mode='lines',
- line=dict(color='#1CAF50'), # Fill between color!
- fill='tozeroy',
- #fillcolor='rgba(226, 241, 248)',
- hoverinfo='skip',
- showlegend=False
- ))
+ # Separate Behandlung für positive und negative Bereiche
+ y_array = np.array(y_smooth)
+ x_array = np.array(x_smooth)
- # Hauptlinie (geglättet)
+ # Positive Bereiche (Anstiege) - Gradient von transparent unten zu grün oben
+ positive_mask = y_array >= 0
+ if np.any(positive_mask):
+ # Nulllinie für positive Bereiche
+ fig.add_trace(go.Scatter(
+ x=x_array,
+ y=np.zeros_like(y_array),
+ mode='lines',
+ line=dict(width=0),
+ hoverinfo='skip',
+ showlegend=False
+ ))
+
+ # Positive Bereiche mit Gradient nach oben
+ fig.add_trace(go.Scatter(
+ x=x_array,
+ y=np.where(y_array >= 0, y_array, 0), # Nur positive Werte
+ fill='tonexty', # Fill zur vorherigen Trace (Nulllinie)
+ mode='lines',
+ line=dict(width=0),
+ fillgradient=dict(
+ type="vertical",
+ colorscale=[
+ (0.0, "rgba(17, 17, 17, 0.0)"), # Transparent unten (bei y=0)
+ (1.0, "rgba(43, 82, 144, 0.8)") # Blau oben (bei maximaler Höhe) Grün: 73, 189, 115
+ ]
+ ),
+ hoverinfo='skip',
+ showlegend=False
+ ))
+
+ # Negative Bereiche (Abstiege) - Gradient von grün unten zu transparent oben
+ negative_mask = y_array < 0
+ if np.any(negative_mask):
+ # Nulllinie für negative Bereiche
+ fig.add_trace(go.Scatter(
+ x=x_array,
+ y=np.where(y_array < 0, y_array, 0), # Nur negative Werte
+ mode='lines',
+ line=dict(width=0),
+ hoverinfo='skip',
+ showlegend=False
+ ))
+
+ # Negative Bereiche mit Gradient nach unten
+ fig.add_trace(go.Scatter(
+ x=x_array,
+ y=np.zeros_like(y_array),
+ fill='tonexty', # Fill zur vorherigen Trace (negative Werte)
+ mode='lines',
+ line=dict(width=0),
+ fillgradient=dict(
+ type="vertical",
+ colorscale=[
+ (0.0, "rgba(43, 82, 144, 0.8)"), # Blau unten (bei minimaler Tiefe)
+ (1.0, "rgba(17, 17, 17, 0.0)") # Transparent oben (bei y=0)
+ ]
+ ),
+ hoverinfo='skip',
+ showlegend=False
+ ))
+
+ # Hauptlinie (geglättet) - über allem
fig.add_trace(go.Scatter(
- x=x_smooth, y=y_smooth,
+ x=x_smooth,
+ y=y_smooth,
mode='lines',
- line=dict(color='#084C20', width=2), # Line color!
+ line=dict(color='#2b5290', width=2), # Weiße Linie für bessere Sichtbarkeit
name='Elevation',
showlegend=False
))
- # SUPDER IDEE, ABER GEHT NICHT WEGEN NEUEN smoothed POINTS! GEHT NUR BEI X
- #fig.update_traces(
- # hovertemplate=(
- # #"Time: %{customdata[0]}
" +
- # "Distance (km): %{customdata[0]:.2f}
" +
- # "Elevation: %{customdata[1]}" +
- # "Elapsed Time: %{customdata[2]}"
- # ),
- # customdata=df[['cum_dist_km','elev', 'time']]
- #
+ # 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'
+ )
# Layout im Dark Theme
fig.update_layout(
- title=dict(text='Höhenprofil relativ zum Startwert', font=dict(size=16, color='white')),
+ 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)',
template='plotly_dark',
@@ -507,6 +556,92 @@ def create_elevation_plot(df, smooth_points=500):
return fig
+# Alte Version - normaler gradient fill between:
+# def create_elevation_plot(df, smooth_points=500):
+# # Originale Daten
+# x = df['time']
+# y = df['rel_elev']
+#
+# # Einfache Glättung: nur Y-Werte glätten, X-Werte beibehalten
+# if len(y) >= 4: # Genug Punkte für cubic interpolation
+# y_numeric = y.to_numpy()
+#
+# # Nur gültige Y-Punkte für Interpolation
+# mask = ~np.isnan(y_numeric)
+#
+# if np.sum(mask) >= 4: # Genug gültige Punkte
+# # Index-basierte Interpolation für Y-Werte
+# valid_indices = np.where(mask)[0]
+# valid_y = y_numeric[mask]
+#
+# # Interpolation über die Indizes
+# f = interp1d(valid_indices, valid_y, kind='cubic',
+# bounds_error=False, fill_value='extrapolate')
+#
+# # Neue Y-Werte für alle ursprünglichen X-Positionen
+# all_indices = np.arange(len(y))
+# y_smooth = f(all_indices)
+#
+# # Originale X-Werte beibehalten
+# x_smooth = x
+# else:
+# # Fallback: originale Daten
+# x_smooth, y_smooth = x, y
+# else:
+# # Zu wenige Punkte: originale Daten verwenden
+# x_smooth, y_smooth = x, y
+#
+# fig = go.Figure()
+#
+# # Fläche unter der Kurve (mit geglätteten Daten)
+# fig.add_trace(go.Scatter(
+# x=x_smooth, y=y_smooth,
+# mode='lines',
+# line=dict(color='#1CAF50'), # Fill between color!
+# fill='tozeroy',
+# #fillcolor='rgba(226, 241, 248)',
+# hoverinfo='skip',
+# showlegend=False
+# ))
+#
+# # Hauptlinie (geglättet)
+# fig.add_trace(go.Scatter(
+# x=x_smooth, y=y_smooth,
+# mode='lines',
+# line=dict(color='#084C20', width=2), # Line color!
+# name='Elevation',
+# showlegend=False
+# ))
+#
+# # SUPDER IDEE, ABER GEHT NICHT WEGE NEUEN smoothed POINTS! GEHT NUR BEI X
+# #fig.update_traces(
+# # hovertemplate=(
+# # #"Time: %{customdata[0]}
" +
+# # "Distance (km): %{customdata[0]:.2f}
" +
+# # "Elevation: %{customdata[1]}" +
+# # "Elapsed Time: %{customdata[2]}"
+# # ),
+# # customdata=df[['cum_dist_km','elev', 'time']]
+# #
+#
+# # Layout im Dark Theme
+# fig.update_layout(
+# title=dict(text='Höhenprofil relativ zum Startwert', font=dict(size=16, color='white')),
+# xaxis_title='Zeit',
+# yaxis_title='Höhe relativ zum Start (m)',
+# template='plotly_dark',
+# paper_bgcolor='#1e1e1e',
+# plot_bgcolor='#111111',
+# font=dict(color='white'),
+# margin=dict(l=40, r=40, t=50, b=40),
+# height=400
+# )
+#
+# return fig
+# #####################
+
+
+
def create_deviation_plot(df): #Distanz-Zeit-Diagramm
@@ -613,7 +748,7 @@ def create_heart_rate_plot(df):
y=df['hr_smooth'][~mask],
mode='lines',
#name='Geglättete Herzfrequenz',
- line=dict(color='#E43D70', width=2),
+ line=dict(color='#ff2c48', width=2),
showlegend=False,
hovertemplate=(
"Zeit: %{x}
" +
@@ -648,7 +783,7 @@ def create_heart_rate_plot(df):
# Annotation für Durchschnittswert
fig.add_annotation(
- x=df['time_loc'].iloc[int(len(df) * 0.8)], # Bei 80% der Zeit
+ x=df['time_loc'].iloc[int(len(df) * 0.5)], # Bei 50% der Zeit
y=mean_hr,
text=f"Ø {mean_hr:.0f} bpm",
showarrow=True,
@@ -664,13 +799,20 @@ def create_heart_rate_plot(df):
# Geschätzte maximale Herzfrequenz (Beispiel: 200 bpm)
max_hr_estimated = 200 # oder z. B. 220 - alter
- # Definiere feste HR-Zonen in BPM
+ ## Definiere feste HR-Zonen in BPM
+ #zones = [
+ # {"name": "Zone 1", "lower": 0, "upper": 124, "color": "#F4A4A3"}, # Regeneration (Recovery)
+ # {"name": "Zone 2", "lower": 124, "upper": 154, "color": "#EF7476"}, # Grundlagenausdauer (Endurance)
+ # {"name": "Zone 3", "lower": 154, "upper": 169, "color": "#EA4748"}, # Tempo (Aerob)
+ # {"name": "Zone 4", "lower": 169, "upper": 184, "color": "#E02628"}, # Schwelle (Threshold) (Anaerob)
+ # {"name": "Zone 5", "lower": 184, "upper": max_hr_estimated, "color": "#B71316"}, # Neuromuskulär (Neuromuskulär)
+ #]
zones = [
- {"name": "Zone 1", "lower": 0, "upper": 124, "color": "#F4A4A3"}, # Regeneration (Recovery)
- {"name": "Zone 2", "lower": 124, "upper": 154, "color": "#EF7476"}, # Grundlagenausdauer (Endurance)
- {"name": "Zone 3", "lower": 154, "upper": 169, "color": "#EA4748"}, # Tempo
- {"name": "Zone 4", "lower": 169, "upper": 184, "color": "#E02628"}, # Schwelle (Threshold)
- {"name": "Zone 5", "lower": 184, "upper": max_hr_estimated, "color": "#B71316"}, # Anaerob
+ {"name": "Zone 1", "lower": 0, "upper": 124, "color": "#4A4A4A"}, # Regeneration (Recovery) (#111111 Transparent)
+ {"name": "Zone 2", "lower": 124, "upper": 154, "color": "#87CEFA"}, # Grundlagenausdauer (Endurance)
+ {"name": "Zone 3", "lower": 154, "upper": 169, "color": "#90EE90"}, # Tempo (Aerob)
+ {"name": "Zone 4", "lower": 169, "upper": 184, "color": "#FFDAB9"}, # Schwelle (Threshold) (Anaerob)
+ {"name": "Zone 5", "lower": 184, "upper": max_hr_estimated, "color": "#FFB6C1"}, # Neuromuskulär (Neuromuskulär)
]
# Zeichne Zonen als Hintergrund (horizontale Rechtecke)
@@ -698,7 +840,7 @@ def create_heart_rate_plot(df):
),
yaxis=dict(
title='Herzfrequenz (bpm)',
- rangemode='tozero'
+ range=[80, 200] # Statt rangemode='tozero'
),
template='plotly_dark',
paper_bgcolor='#1e1e1e',
@@ -762,13 +904,14 @@ def create_pace_bars_plot(df):
# Step 4: Create Plotly bar chart
fig = go.Figure()
fig.add_trace(go.Bar(
- x=df['km_start'],
+ x=df['km_start'], # Mittig unter jeder Bar
y=df['pace_min_per_km'],
width=df['segment_len'],
text=[f"{v:.1f} min/km" if pd.notnull(v) else "" for v in df['pace_min_per_km']],
#textposition='outside',
textposition='inside',
marker_color='#125595',
+ opacity=0.9, # Transparenz
name='Pace pro km',
offset=0
))