From 825cf388b55a6c8441e02149749bb6e2968477c0 Mon Sep 17 00:00:00 2001 From: Martin Kagamino Lehoux Date: Thu, 27 Aug 2026 11:21:17 +0200 Subject: Extract climb detection into a dedicated climb package Move climb detection, slope, and Cotacol scoring out of ride into a new climb package that operates on a geometry-only Route (distances, elevations, coords). Ride embeds climb.Route and keeps its timing and telemetry columns. - climb.Detect returns []DetectedClimb (Climb + ride-absolute Start). - Climb owns its sliced geometry; indices are local, with Start reported at the detection boundary for absolute position. - Detected bounds use core.Interval; freeRegions claim suppression fixed. - ride no longer builds slices: ClimbFromDist/ClimbFromIndexes/climbFrom and ride.Climb/Interval/Range are removed; rideanalysis, mountain_pass MatchClimb, and web views are rewired to climb types. - New internal/routetest builds climb.Route from the ride DSL; ridetest reuses it and adds timestamps and telemetry. - Tests moved to climb (synthetic detection and difficulty) and kept in ride for GPX-example integration; ride.comparison detection coverage retained. - mountain_pass.nearestCrossing now reuses geo.Nearest. --- mountain_pass/detection.go | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) (limited to 'mountain_pass/detection.go') diff --git a/mountain_pass/detection.go b/mountain_pass/detection.go index 40281f3..79e55e4 100644 --- a/mountain_pass/detection.go +++ b/mountain_pass/detection.go @@ -5,7 +5,8 @@ import ( "math" "github.com/jftuga/geodist" - "github.com/martinlehoux/biking_home/ride" + "github.com/martinlehoux/biking_home/climb" + "github.com/martinlehoux/biking_home/geo" ) // MountainPass is a pass extracted from OpenStreetMap: a node tagged @@ -112,9 +113,9 @@ func DetectCrossings(coords []geodist.Coord, distances, elevations []float64, pa // MatchClimb returns the pass whose coordinates lie within radiusM of the // climb's highest point and whose elevation is within elevationToleranceM of // that point's elevation, nearest first. Found is false when no pass matches. -func MatchClimb(climb ride.Climb, passes []MountainPass, radiusM, elevationToleranceM float64) (MountainPass, bool) { - topCoord := climb.TopCoord() - topElevation := climb.TopElevationM() +func MatchClimb(segment climb.Climb, passes []MountainPass, radiusM, elevationToleranceM float64) (MountainPass, bool) { + topIndex, topElevation := segment.Top() + topCoord := segment.Coords[topIndex] var best MountainPass bestDistanceM := radiusM found := false @@ -139,21 +140,17 @@ func MatchClimb(climb ride.Climb, passes []MountainPass, radiusM, elevationToler } func nearestCrossing(coords []geodist.Coord, distances, elevations []float64, mountainPass MountainPass) (Crossing, bool) { - best := Crossing{Pass: mountainPass, DistanceToM: 1e18} - for i := range coords { - distanceKm, _ := geodist.HaversineDistance(coords[i], *mountainPass.Coord) - distanceM := distanceKm * 1000 - if distanceM < best.DistanceToM { - best.DistanceToM = distanceM - best.RideDistanceM = distances[i] - best.RideElevation = elevations[i] - best.ElevationDiff = absFloat64(elevations[i] - float64(mountainPass.Elevation)) - } - } - if best.DistanceToM > 1e17 { - return best, false + index, distanceM := geo.Nearest(coords, *mountainPass.Coord) + if math.IsInf(distanceM, 1) { + return Crossing{Pass: mountainPass}, false } - return best, true + return Crossing{ + Pass: mountainPass, + DistanceToM: distanceM, + RideDistanceM: distances[index], + RideElevation: elevations[index], + ElevationDiff: absFloat64(elevations[index] - float64(mountainPass.Elevation)), + }, true } func absFloat64(value float64) float64 { -- cgit v1.3