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. --- web/rides.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'web/rides.go') diff --git a/web/rides.go b/web/rides.go index 71ab3fd..d3f5634 100644 --- a/web/rides.go +++ b/web/rides.go @@ -6,6 +6,7 @@ import ( "net/http" "strconv" + "github.com/martinlehoux/biking_home/climb" "github.com/martinlehoux/biking_home/mountain_pass" "github.com/martinlehoux/biking_home/official_climb" "github.com/martinlehoux/biking_home/ride" @@ -65,7 +66,7 @@ func (s *Server) handleRide(w http.ResponseWriter, r *http.Request) { }), w) return } - passes, err := mountain_pass.LoadMountainPassesAroundRide(s.db, parsed.Coords(), rideDetailMountainPassMarginM) + passes, err := mountain_pass.LoadMountainPassesAroundRide(s.db, parsed.Coords, rideDetailMountainPassMarginM) if err != nil { slog.Warn("Failed to load nearby mountain passes for ride detail", "ride_id", id, "error", err) } @@ -144,7 +145,7 @@ func (s *Server) handleCreateOfficialClimb(w http.ResponseWriter, r *http.Reques http.Error(w, "the recorded route is unavailable", http.StatusBadRequest) return } - bounds := parsed.Coords() + bounds := parsed.Coords if startIndex >= endIndex || endIndex >= len(bounds) { http.Error(w, "the official climb boundaries must be ordered route points", http.StatusBadRequest) return @@ -192,7 +193,7 @@ func buildRideViews(items []ride.StoredRide) []RideView { func buildRideView(item ride.StoredRide) RideView { view := RideView{Ride: item, Cotacol: "-", CotacolPer100Km: "-", MovingTime: formatDuration(item.EffectiveMovingTimeS())} cotacol, ready := item.CotacolScore() - if ready && item.CotacolAlgorithmVersion() == ride.CotacolAlgorithmVersion && item.DistanceM > 0 { + if ready && item.CotacolAlgorithmVersion() == climb.CotacolAlgorithmVersion && item.DistanceM > 0 { view.Cotacol = formatCotacol(cotacol) view.CotacolPer100Km = formatCotacolPer100Km(cotacol, item.DistanceM) } -- cgit v1.3