aboutsummaryrefslogtreecommitdiff
path: root/web/climbs.go
diff options
context:
space:
mode:
Diffstat (limited to 'web/climbs.go')
-rw-r--r--web/climbs.go70
1 files changed, 52 insertions, 18 deletions
diff --git a/web/climbs.go b/web/climbs.go
index 6416509..5cd6db9 100644
--- a/web/climbs.go
+++ b/web/climbs.go
@@ -38,6 +38,18 @@ type ClimbAttemptView struct {
Duration string
Distance string
Cotacol string
+ // Numeric chart data; AveragePowerW is nil for rides without a power meter.
+ DurationS float64
+ AveragePowerW *float64
+}
+
+// ClimbAttemptPoint is one dot on the attempts chart: attempt duration against average power.
+type ClimbAttemptPoint struct {
+ DurationS float64 `json:"durationS"`
+ AveragePowerW float64 `json:"averagePowerW"`
+ Date string `json:"date"`
+ RideName string `json:"rideName"`
+ URL string `json:"url"`
}
type ClimbStatsView struct {
@@ -51,11 +63,12 @@ type ClimbStatsView struct {
}
type ClimbDetailPageData struct {
- Climb official_climb.OfficialClimb
- Stats ClimbStatsView
- Attempts []ClimbAttemptView
- Profile ClimbProfileData
- Endpoints ClimbMapEndpoints
+ Climb official_climb.OfficialClimb
+ Stats ClimbStatsView
+ Attempts []ClimbAttemptView
+ AttemptPoints []ClimbAttemptPoint
+ Profile ClimbProfileData
+ Endpoints ClimbMapEndpoints
}
type ClimbMapEndpoints struct {
@@ -88,11 +101,13 @@ func (s *Server) handleClimb(w http.ResponseWriter, r *http.Request) {
}
aggregate := official_climb.AggregateAttempts(attempts)
profile := s.buildClimbProfile(climb, aggregate, attempts)
+ views := buildClimbAttemptViews(s.db, attempts)
kcore.RenderPage(r.Context(), ClimbDetailPage(ClimbDetailPageData{
- Climb: climb,
- Stats: buildClimbStatsView(aggregate),
- Attempts: buildClimbAttemptViews(s.db, attempts),
- Profile: profile,
+ Climb: climb,
+ Stats: buildClimbStatsView(aggregate),
+ Attempts: views,
+ AttemptPoints: climbAttemptPoints(views),
+ Profile: profile,
Endpoints: ClimbMapEndpoints{
Start: []float64{climb.StartCoord.Lat, climb.StartCoord.Lon},
End: []float64{climb.EndCoord.Lat, climb.EndCoord.Lon},
@@ -126,11 +141,10 @@ func (s *Server) buildClimbProfile(officialClimb official_climb.OfficialClimb, a
continue
}
for _, matched := range official_climb.MatchClimbs(parsed.Route, officialClimb, matchPolicy.EndpointRadiusM) {
- segment := climb.Climb{Route: climb.Route{
+ segment := climb.Climb{
Distances: parsed.Distances[matched.StartIndex : matched.EndIndex+1],
Elevations: parsed.Elevations[matched.StartIndex : matched.EndIndex+1],
- Coords: parsed.Coords[matched.StartIndex : matched.EndIndex+1],
- }}
+ Coords: parsed.Coords[matched.StartIndex : matched.EndIndex+1]}
if sample := official_climb.NormalizeProfile(segment); len(sample) > 0 {
elevationSamples = append(elevationSamples, sample)
}
@@ -199,17 +213,37 @@ func buildClimbAttemptViews(db *sql.DB, attempts []official_climb.Attempt) []Cli
names[attempt.RideID] = name
}
views[i] = ClimbAttemptView{
- RideID: attempt.RideID,
- RideName: name,
- Date: formatRideDate(attempt.StartedAt),
- Duration: formatDuration(int64(attempt.EndedAt.Sub(attempt.StartedAt).Seconds())),
- Distance: formatDistance(attempt.DistanceM),
- Cotacol: formatCotacol(attempt.Cotacol),
+ RideID: attempt.RideID,
+ RideName: name,
+ Date: formatRideDate(attempt.StartedAt),
+ Duration: formatDuration(int64(attempt.EndedAt.Sub(attempt.StartedAt).Seconds())),
+ Distance: formatDistance(attempt.DistanceM),
+ Cotacol: formatCotacol(attempt.Cotacol),
+ DurationS: attempt.EndedAt.Sub(attempt.StartedAt).Seconds(),
+ AveragePowerW: attempt.AveragePowerW,
}
}
return views
}
+// climbAttemptPoints maps attempts to chart dots; an attempt without power data carries no dot.
+func climbAttemptPoints(views []ClimbAttemptView) []ClimbAttemptPoint {
+ points := make([]ClimbAttemptPoint, 0, len(views))
+ for _, view := range views {
+ if view.AveragePowerW == nil {
+ continue
+ }
+ points = append(points, ClimbAttemptPoint{
+ DurationS: view.DurationS,
+ AveragePowerW: *view.AveragePowerW,
+ Date: view.Date,
+ RideName: view.RideName,
+ URL: rideDetailURL(view.RideID),
+ })
+ }
+ return points
+}
+
func climbsListURL() string {
return "/climbs"
}