diff options
Diffstat (limited to 'web/climbs.go')
| -rw-r--r-- | web/climbs.go | 70 |
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" } |
