summaryrefslogtreecommitdiff
path: root/mountain_pass/detection_test.go
diff options
context:
space:
mode:
authorMartin Kagamino Lehoux <martin@lehoux.net>2026-08-03 08:25:07 +0200
committerMartin Kagamino Lehoux <martin@lehoux.net>2026-08-03 08:25:07 +0200
commit9707f9ad09db935af7d06b5de08bc52de13ba6a7 (patch)
tree34bcd01e5eda302a8730bf0817d5dd7e82186359 /mountain_pass/detection_test.go
parentf06a10982ab9d5aaa7d31cea31964431fa75f253 (diff)
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
Diffstat (limited to 'mountain_pass/detection_test.go')
-rw-r--r--mountain_pass/detection_test.go109
1 files changed, 109 insertions, 0 deletions
diff --git a/mountain_pass/detection_test.go b/mountain_pass/detection_test.go
new file mode 100644
index 0000000..3208061
--- /dev/null
+++ b/mountain_pass/detection_test.go
@@ -0,0 +1,109 @@
+package mountain_pass_test
+
+import (
+ "testing"
+
+ "github.com/jftuga/geodist"
+ "github.com/martinlehoux/biking_home/mountain_pass"
+ "github.com/martinlehoux/biking_home/ride"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestDetectCrossingsFindsPass(t *testing.T) {
+ r := rideFromPoint(t, 43.62, 5.43, 447)
+ pass := mountain_pass.MountainPass{
+ Name: "Pas de Magnan", Elevation: 460,
+ Coord: &geodist.Coord{Lat: 43.62, Lon: 5.43},
+ }
+ crossings := mountain_pass.DetectCrossings(r, []mountain_pass.MountainPass{pass}, 100, 25)
+ assert.Len(t, crossings, 1)
+ assert.Equal(t, "Pas de Magnan", crossings[0].Pass.Name)
+ assert.Less(t, crossings[0].DistanceToM, 10.0)
+ assert.Less(t, crossings[0].ElevationDiff, 25.0)
+}
+
+func TestDetectCrossingsIgnoresDistantPass(t *testing.T) {
+ r := rideFromPoint(t, 43.62, 5.43, 447)
+ far := mountain_pass.MountainPass{
+ Name: "Col de la Faucille", Elevation: 1320,
+ Coord: &geodist.Coord{Lat: 46.37, Lon: 6.02},
+ }
+ assert.Empty(t, mountain_pass.DetectCrossings(r, []mountain_pass.MountainPass{far}, 100, 25))
+}
+
+func TestDetectCrossingsIgnoresPassWithoutCoordinates(t *testing.T) {
+ r := rideFromPoint(t, 43.62, 5.43, 447)
+ noCoord := mountain_pass.MountainPass{Name: "Col de la Gatasse", Elevation: 120}
+ assert.Empty(t, mountain_pass.DetectCrossings(r, []mountain_pass.MountainPass{noCoord}, 100, 25))
+}
+
+func TestDetectCrossingsRequiresElevationAgreement(t *testing.T) {
+ r := rideFromPoint(t, 43.62, 5.43, 447)
+ pass := mountain_pass.MountainPass{
+ Name: "Pas de Magnan", Elevation: 900,
+ Coord: &geodist.Coord{Lat: 43.62, Lon: 5.43},
+ }
+ assert.Empty(t, mountain_pass.DetectCrossings(r, []mountain_pass.MountainPass{pass}, 100, 25))
+}
+
+func rideFromPoint(t *testing.T, latitude, longitude, elevation float64) ride.Ride {
+ t.Helper()
+ points := []ride.Point{
+ {DistanceM: 0, ElevationM: 100, Coord: geodist.Coord{Lat: latitude - 0.01, Lon: longitude - 0.01}},
+ {DistanceM: 1000, ElevationM: elevation, Coord: geodist.Coord{Lat: latitude, Lon: longitude}},
+ }
+ return ride.FromPoints(points)
+}
+
+func TestMatchClimbFindsPassAtTop(t *testing.T) {
+ climb := climbFromPoints([]ride.Point{
+ {DistanceM: 0, ElevationM: 300, Coord: geodist.Coord{Lat: 43.61, Lon: 5.42}},
+ {DistanceM: 1000, ElevationM: 447, Coord: geodist.Coord{Lat: 43.62, Lon: 5.43}},
+ {DistanceM: 2000, ElevationM: 380, Coord: geodist.Coord{Lat: 43.63, Lon: 5.44}},
+ })
+ pass := mountain_pass.MountainPass{
+ Name: "Pas de Magnan", Elevation: 440,
+ Coord: &geodist.Coord{Lat: 43.62, Lon: 5.43},
+ }
+ matched, found := mountain_pass.MatchClimb(climb, []mountain_pass.MountainPass{pass}, 100, 25)
+ assert.True(t, found)
+ assert.Equal(t, "Pas de Magnan", matched.Name)
+}
+
+func TestMatchClimbRequiresElevationAgreement(t *testing.T) {
+ climb := climbFromPoints([]ride.Point{
+ {DistanceM: 0, ElevationM: 300, Coord: geodist.Coord{Lat: 43.61, Lon: 5.42}},
+ {DistanceM: 1000, ElevationM: 447, Coord: geodist.Coord{Lat: 43.62, Lon: 5.43}},
+ {DistanceM: 2000, ElevationM: 380, Coord: geodist.Coord{Lat: 43.63, Lon: 5.44}},
+ })
+ pass := mountain_pass.MountainPass{
+ Name: "Pas de Magnan", Elevation: 900,
+ Coord: &geodist.Coord{Lat: 43.62, Lon: 5.43},
+ }
+ _, found := mountain_pass.MatchClimb(climb, []mountain_pass.MountainPass{pass}, 100, 25)
+ assert.False(t, found)
+}
+
+func TestMatchClimbNearestOfTwo(t *testing.T) {
+ climb := climbFromPoints([]ride.Point{
+ {DistanceM: 0, ElevationM: 300, Coord: geodist.Coord{Lat: 43.61, Lon: 5.42}},
+ {DistanceM: 1000, ElevationM: 447, Coord: geodist.Coord{Lat: 43.62, Lon: 5.43}},
+ {DistanceM: 2000, ElevationM: 380, Coord: geodist.Coord{Lat: 43.63, Lon: 5.44}},
+ })
+ near := mountain_pass.MountainPass{
+ Name: "Pas de Magnan", Elevation: 440,
+ Coord: &geodist.Coord{Lat: 43.62, Lon: 5.43},
+ }
+ far := mountain_pass.MountainPass{
+ Name: "Col lointain", Elevation: 440,
+ Coord: &geodist.Coord{Lat: 43.6201, Lon: 5.4301},
+ }
+ matched, found := mountain_pass.MatchClimb(climb, []mountain_pass.MountainPass{near, far}, 100, 25)
+ assert.True(t, found)
+ assert.Equal(t, "Pas de Magnan", matched.Name)
+}
+
+func climbFromPoints(points []ride.Point) ride.Climb {
+ ride := ride.FromPoints(points)
+ return ride.ClimbFromDist(0, 2000)
+}