From 9707f9ad09db935af7d06b5de08bc52de13ba6a7 Mon Sep 17 00:00:00 2001 From: Martin Kagamino Lehoux Date: Mon, 3 Aug 2026 08:25:07 +0200 Subject: feat: Detect mountain pass crossings, name climbs after passes, render ride charts - osmpass: extract mountain_pass=yes nodes from an OSM PBF, enrich centcols passes with OSM coordinates, resumable -fetch-osm download - mountain_pass: DetectCrossings for a ride, MatchClimb to name a climb after the pass it tops - ride: expose Points(), add Climb.Top() and Climb.Name - chart: -chart renders elevation profile with climb bands and pass markers - commands: -fetch-osm, -extract-osm, -import-cached, -enrich --- ride/climb.go | 19 ++++++++++++++++++- ride/ride.go | 4 ++++ 2 files changed, 22 insertions(+), 1 deletion(-) (limited to 'ride') diff --git a/ride/climb.go b/ride/climb.go index 8ac60fa..d5a1718 100644 --- a/ride/climb.go +++ b/ride/climb.go @@ -16,6 +16,7 @@ type Climb struct { rideStart int rideEnd int points []Point + Name string } func (climb Climb) Duration() time.Duration { @@ -34,11 +35,27 @@ func (climb Climb) End() Point { return climb.points[len(climb.points)-1] } +// Top returns the highest point inside the climb, used as the crest anchor +// when naming the climb after a pass. +func (climb Climb) Top() Point { + top := climb.points[0] + for _, point := range climb.points { + if point.ElevationM > top.ElevationM { + top = point + } + } + return top +} + func (climb Climb) String() string { start := climb.points[0] end := climb.points[len(climb.points)-1] score := Score(climb.points, 0, len(climb.points)-1) - return fmt.Sprintf("%.1fkm-%.1fkm: %.1fkm at %.1f%% (%d pts - %s)", start.DistanceM/1000, end.DistanceM/1000, (end.DistanceM-start.DistanceM)/1000, Slope(start, end)*100, int(score), Category(score)) + body := fmt.Sprintf("%.1fkm-%.1fkm: %.1fkm at %.1f%% (%d pts - %s)", start.DistanceM/1000, end.DistanceM/1000, (end.DistanceM-start.DistanceM)/1000, Slope(start, end)*100, int(score), Category(score)) + if climb.Name == "" { + return body + } + return climb.Name + ": " + body } func (climb Climb) Score() float64 { diff --git a/ride/ride.go b/ride/ride.go index 73e7a26..df5560b 100644 --- a/ride/ride.go +++ b/ride/ride.go @@ -31,6 +31,10 @@ func (r *Ride) check() { kcore.Assert(len(r.points) > 0, "no points in ride") } +func (r *Ride) Points() []Point { + return r.points +} + type RideParser interface { Parse(reader io.Reader) (Ride, error) } -- cgit v1.2.3