From 8a0032d08862dcf28c653bee3fd21052e967e35d Mon Sep 17 00:00:00 2001 From: Martin Kagamino Lehoux Date: Sun, 18 Oct 2026 21:19:36 +0200 Subject: Cut climb profile sections in the backend The chart steps are computed in Go and shipped as finished sections, next to the profile statistics the climb package already owns: climb.ProfileSections cuts a Route at its widest bends under a ProfilePolicy, and rideanalysis.MergeClimbProfile takes over the merge that lived in web/climbs.go, so web only loads the attempt rides and maps the DTOs. The payload carries sections instead of raw elevations, and the browser keeps what needs the canvas: slope bands, labels, distance ticks and colours. official-climb-profile-logic.ts and its tests are gone, replaced by climb/profile_test.go and a page test reading the embedded profile JSON. --- web/views.go | 54 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 13 deletions(-) (limited to 'web/views.go') diff --git a/web/views.go b/web/views.go index af3dbc2..f895fc5 100644 --- a/web/views.go +++ b/web/views.go @@ -123,20 +123,44 @@ type RideProfilePoint struct { } type RideProfileClimb struct { - Index int `json:"-"` + Index int `json:"index"` + StartKm float64 `json:"startKm"` + EndKm float64 `json:"endKm"` + TopKm float64 `json:"topKm"` + TopElevationM float64 `json:"topElevationM"` + Name string `json:"name"` + Category string `json:"category"` + DistanceKm float64 `json:"distanceKm"` + SlopePercent float64 `json:"slopePercent"` + Cotacol float64 `json:"cotacol"` + OfficialClimbID int64 `json:"officialClimbId,omitempty"` + OfficialName string `json:"officialName,omitempty"` + StartIndex int `json:"startIndex"` + EndIndex int `json:"endIndex"` + Sections []ProfileSectionView `json:"sections,omitempty"` +} + +// ProfileSectionView is one step of a drawn climb profile: the stretch between two bends of the trace. +type ProfileSectionView struct { StartKm float64 `json:"startKm"` EndKm float64 `json:"endKm"` - TopKm float64 `json:"topKm"` - TopElevationM float64 `json:"topElevationM"` - Name string `json:"name"` - Category string `json:"category"` - DistanceKm float64 `json:"distanceKm"` + StartElevationM float64 `json:"startElevationM"` + EndElevationM float64 `json:"endElevationM"` SlopePercent float64 `json:"slopePercent"` - Cotacol float64 `json:"cotacol"` - OfficialClimbID int64 `json:"officialClimbId,omitempty"` - OfficialName string `json:"officialName,omitempty"` - StartIndex int `json:"startIndex"` - EndIndex int `json:"endIndex"` +} + +func profileSectionsView(sections []climb.ProfileSection) []ProfileSectionView { + views := make([]ProfileSectionView, 0, len(sections)) + for _, section := range sections { + views = append(views, ProfileSectionView{ + StartKm: section.StartDistanceM / 1000, + EndKm: section.EndDistanceM / 1000, + StartElevationM: section.StartElevationM, + EndElevationM: section.EndElevationM, + SlopePercent: section.SlopePercent, + }) + } + return views } type RideProfileCrossing struct { @@ -366,10 +390,11 @@ func buildRideProfile(parsed ride.Ride, passes []mountain_pass.MountainPass, off } } analysis := rideanalysis.Analyze(parsed, passes, officialClimbs, matchPolicy) + profilePolicy := climb.DefaultProfilePolicy() for _, analyzedClimb := range analysis.Climbs { segment := analyzedClimb.Segment topIndex, topElevationM := segment.Top() - climb := RideProfileClimb{ + climbView := RideProfileClimb{ Index: len(profile.Climbs), StartKm: segment.Distances[0] / 1000, EndKm: segment.Distances[len(segment.Distances)-1] / 1000, @@ -385,7 +410,10 @@ func buildRideProfile(parsed ride.Ride, passes []mountain_pass.MountainPass, off StartIndex: analyzedClimb.Interval.Start, EndIndex: analyzedClimb.Interval.End, } - profile.Climbs = append(profile.Climbs, climb) + if analyzedClimb.OfficialClimbID > 0 { + climbView.Sections = profileSectionsView(segment.ProfileSections(profilePolicy)) + } + profile.Climbs = append(profile.Climbs, climbView) } for _, crossing := range analysis.Crossings { profile.Crossings = append(profile.Crossings, RideProfileCrossing{ -- cgit v1.3