diff options
| author | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-08-14 19:49:18 +0200 |
|---|---|---|
| committer | Martin Kagamino Lehoux <martin@lehoux.net> | 2026-08-14 19:49:18 +0200 |
| commit | 9b62eec13fb391a3055253422cc58252db820d99 (patch) | |
| tree | 469e4b1d64258bd226ffe97dfc481eaffc72aa07 /rideanalysis/analysis.go | |
| parent | 3eebdfbe4a6033a0bef0acdaa13061dd830c65d6 (diff) | |
refactor: extract ride analysis
Diffstat (limited to 'rideanalysis/analysis.go')
| -rw-r--r-- | rideanalysis/analysis.go | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/rideanalysis/analysis.go b/rideanalysis/analysis.go new file mode 100644 index 0000000..3d34855 --- /dev/null +++ b/rideanalysis/analysis.go @@ -0,0 +1,95 @@ +package rideanalysis + +import ( + "math" + + "github.com/jftuga/geodist" + "github.com/martinlehoux/biking_home/mountain_pass" + "github.com/martinlehoux/biking_home/official_climb" + "github.com/martinlehoux/biking_home/ride" +) + +type Result struct { + Climbs []AnalyzedClimb + Crossings []mountain_pass.Crossing +} + +type AnalyzedClimb struct { + Segment ride.Climb + Name string + Score float64 + Category string + DistanceM float64 + SlopePercent float64 + Cotacol float64 + OfficialClimbID int64 + OfficialName string +} + +func Analyze(parsed ride.Ride, passes []mountain_pass.MountainPass, officialClimbs []official_climb.OfficialClimb, matchPolicy official_climb.MatchPolicy) Result { + result := Result{ + Climbs: make([]AnalyzedClimb, 0), + Crossings: make([]mountain_pass.Crossing, 0), + } + for _, detectedClimb := range parsed.AllClimbs() { + displayClimb := detectedClimb + if matchedPass, ok := mountain_pass.MatchClimb(displayClimb, passes, 300, 50); ok { + displayClimb.Name = matchedPass.Name + } + matchedOfficial, officialFound := official_climb.MatchClimb(detectedClimb, officialClimbs, matchPolicy) + if officialFound { + startIndex := nearestCoordIndex(parsed, matchedOfficial.StartCoord) + endIndex := nearestCoordIndex(parsed, matchedOfficial.EndCoord) + if startIndex < endIndex { + displayClimb = parsed.ClimbFromIndexes(startIndex, endIndex) + displayClimb.Name = matchedOfficial.Name + } else { + officialFound = false + matchedOfficial = official_climb.OfficialClimb{} + } + } + score := displayClimb.Score() + result.Climbs = append(result.Climbs, AnalyzedClimb{ + Segment: displayClimb, + Name: displayClimb.Name, + Score: score, + Category: ride.Category(score), + DistanceM: displayClimb.EndDistanceM() - displayClimb.StartDistanceM(), + SlopePercent: ride.Slope(parsed, displayClimb.StartIndex(), displayClimb.EndIndex()) * 100, + Cotacol: displayClimb.DifficultyScore(), + OfficialClimbID: officialClimbID(matchedOfficial, officialFound), + OfficialName: officialClimbName(matchedOfficial, officialFound), + }) + } + for _, crossing := range mountain_pass.DetectCrossings(parsed, passes, 100, 25) { + result.Crossings = append(result.Crossings, crossing) + } + return result +} + +func nearestCoordIndex(parsed ride.Ride, target geodist.Coord) int { + bestIndex := 0 + bestDistance := math.Inf(1) + for index := 0; index < parsed.Len(); index++ { + _, distanceKm := geodist.HaversineDistance(parsed.Coord(index), target) + if distanceKm < bestDistance { + bestDistance = distanceKm + bestIndex = index + } + } + return bestIndex +} + +func officialClimbID(climb official_climb.OfficialClimb, found bool) int64 { + if !found { + return 0 + } + return climb.ID +} + +func officialClimbName(climb official_climb.OfficialClimb, found bool) string { + if !found { + return "" + } + return climb.Name +} |