summaryrefslogtreecommitdiff
path: root/ride
diff options
context:
space:
mode:
Diffstat (limited to 'ride')
-rw-r--r--ride/.snapshots/TestRideClimbLaCride6
-rw-r--r--ride/climb.go47
-rw-r--r--ride/climb_test.go42
-rw-r--r--ride/ride.go21
4 files changed, 48 insertions, 68 deletions
diff --git a/ride/.snapshots/TestRideClimbLaCride b/ride/.snapshots/TestRideClimbLaCride
index 456818e..d81ca17 100644
--- a/ride/.snapshots/TestRideClimbLaCride
+++ b/ride/.snapshots/TestRideClimbLaCride
@@ -1,5 +1,5 @@
([]string) (len=3) {
- (string) (len=69) "Climb 17.1km-18.8km: 1.7km at 4.8% (38 pts - Cat 4) (5m50s, 17.0km/h)",
- (string) (len=65) " 17.2km-18.8km: 1.6km at 4.9% (38 pts - Cat 4) (7m42s, 12.8km/h)",
- (string) (len=70) "Climb 35.1km-40.1km: 5.1km at 4.3% (94 pts - Cat 3) (18m48s, 16.2km/h)"
+ (string) (len=69) "Climb 17.1km-18.8km: 1.7km at 4.8% (45 pts - Cat 4) (5m50s, 17.0km/h)",
+ (string) (len=65) " 17.2km-18.8km: 1.6km at 4.9% (45 pts - Cat 4) (7m42s, 12.8km/h)",
+ (string) (len=71) "Climb 35.1km-40.1km: 5.1km at 4.3% (126 pts - Cat 3) (18m48s, 16.2km/h)"
}
diff --git a/ride/climb.go b/ride/climb.go
index 788cd61..56e47c6 100644
--- a/ride/climb.go
+++ b/ride/climb.go
@@ -79,18 +79,14 @@ func (climb Climb) TopCoord() geodist.Coord {
func (climb Climb) String() string {
startDistance := climb.StartDistanceM()
endDistance := climb.EndDistanceM()
- score := climb.Score()
- body := fmt.Sprintf("%.1fkm-%.1fkm: %.1fkm at %.1f%% (%d pts - %s)", startDistance/1000, endDistance/1000, (endDistance-startDistance)/1000, Slope(climb.ride, climb.rideStart, climb.rideEnd)*100, int(score), Category(score))
+ cotacol := climb.DifficultyScore()
+ body := fmt.Sprintf("%.1fkm-%.1fkm: %.1fkm at %.1f%% (%d pts - %s)", startDistance/1000, endDistance/1000, (endDistance-startDistance)/1000, Slope(climb.ride, climb.rideStart, climb.rideEnd)*100, int(cotacol), Category(cotacol))
if climb.Name == "" {
return body
}
return climb.Name + ": " + body
}
-func (climb Climb) Score() float64 {
- return Score(climb.ride, climb.rideStart, climb.rideEnd)
-}
-
func (climb Climb) DifficultyScore() float64 {
return difficultyScore(climb.ride, climb.rideStart, climb.rideEnd)
}
@@ -99,28 +95,27 @@ func Slope(r Ride, start, end int) float64 {
return (r.ElevationM(end) - r.ElevationM(start)) / (r.DistanceM(end) - r.DistanceM(start))
}
-func Score(r Ride, start, end int) float64 {
- kcore.Assert(end > start, "no points for score")
+func climbDetectionScore(r Ride, start, end int) float64 {
+ kcore.Assert(end > start, "no points for climb detection")
distance := r.DistanceM(end) - r.DistanceM(start)
if distance == 0 {
return 0
}
dElevation := r.ElevationM(end) - r.ElevationM(start)
-
return math.Abs(dElevation) * dElevation / distance * 100.0 * 100.0 / 1000.0
}
-func Category(score float64) string {
+func Category(cotacol float64) string {
switch {
- case score < 35:
+ case cotacol < 35:
return "NO"
- case score < 80:
+ case cotacol < 80:
return "Cat 4"
- case score < 180:
+ case cotacol < 180:
return "Cat 3"
- case score < 250:
+ case cotacol < 250:
return "Cat 2"
- case score < 600:
+ case cotacol < 600:
return "Cat 1"
default:
return "HC"
@@ -130,28 +125,28 @@ func Category(score float64) string {
func bestClimbBetween(r Ride, start, end int) Climb {
kcore.Assert(end > start, "empty points")
- bestScore := Score(r, start, end)
+ bestDetectionScore := climbDetectionScore(r, start, end)
bestStart := start
for i := start; i < end; i++ {
- score := Score(r, i, end)
- if score > bestScore {
+ detectionScore := climbDetectionScore(r, i, end)
+ if detectionScore > bestDetectionScore {
bestStart = i
- bestScore = score
+ bestDetectionScore = detectionScore
}
}
bestEnd := end
for i := end; i > bestStart; i-- {
- score := Score(r, bestStart, i)
- if score > bestScore {
+ detectionScore := climbDetectionScore(r, bestStart, i)
+ if detectionScore > bestDetectionScore {
bestEnd = i
- bestScore = score
+ bestDetectionScore = detectionScore
}
}
for i := bestStart; i < bestEnd; i++ {
- score := Score(r, i, bestEnd)
- if score > bestScore {
+ detectionScore := climbDetectionScore(r, i, bestEnd)
+ if detectionScore > bestDetectionScore {
bestStart = i
- bestScore = score
+ bestDetectionScore = detectionScore
}
}
kcore.Assert(bestStart < bestEnd, "empty climb")
@@ -175,7 +170,7 @@ func climbsBetween(r Ride, start, end int) []Climb {
return climbsBetween(r, start+1, end)
}
climb := bestClimbBetween(r, start, highest)
- if climb.Score() >= 35 && climb.EndDistanceM()-climb.StartDistanceM() >= ClimbDistanceMinimum {
+ if climbDetectionScore(r, climb.rideStart, climb.rideEnd) >= 35 && climb.EndDistanceM()-climb.StartDistanceM() >= ClimbDistanceMinimum {
slog.Debug("Found climb between", slog.Int("start", int(climb.StartDistanceM())), slog.Int("end", int(climb.EndDistanceM())))
climbs = append(climbs, climb)
}
diff --git a/ride/climb_test.go b/ride/climb_test.go
index 3dd6e53..feca001 100644
--- a/ride/climb_test.go
+++ b/ride/climb_test.go
@@ -86,12 +86,12 @@ func TestPogacar20220721(t *testing.T) {
climbs := r.AllClimbs()
assert.Len(t, climbs, 6)
- assert.Equal(t, "0.6km-5.6km: 5.0km at 3.5% (62 pts - Cat 4)", climbs[0].String())
- assert.Equal(t, "42.9km-45.2km: 2.3km at 5.0% (59 pts - Cat 4)", climbs[1].String())
- assert.Equal(t, "60.0km-76.4km: 16.4km at 7.2% (854 pts - HC)", climbs[2].String())
- assert.Equal(t, "83.8km-85.9km: 2.0km at 5.4% (59 pts - Cat 4)", climbs[3].String())
- assert.Equal(t, "99.2km-109.3km: 10.2km at 8.4% (710 pts - HC)", climbs[4].String())
- assert.Equal(t, "128.6km-142.2km: 13.6km at 7.8% (832 pts - HC)", climbs[5].String())
+ assert.Equal(t, "0.6km-5.6km: 5.0km at 3.5% (92 pts - Cat 3)", climbs[0].String())
+ assert.Equal(t, "42.9km-45.2km: 2.3km at 5.0% (74 pts - Cat 4)", climbs[1].String())
+ assert.Equal(t, "60.0km-76.4km: 16.4km at 7.2% (977 pts - HC)", climbs[2].String())
+ assert.Equal(t, "83.8km-85.9km: 2.0km at 5.4% (79 pts - Cat 4)", climbs[3].String())
+ assert.Equal(t, "99.2km-109.3km: 10.2km at 8.4% (751 pts - HC)", climbs[4].String())
+ assert.Equal(t, "128.6km-142.2km: 13.6km at 7.8% (930 pts - HC)", climbs[5].String())
}
func TestBouclesVerdon2024(t *testing.T) {
@@ -100,12 +100,12 @@ func TestBouclesVerdon2024(t *testing.T) {
climbs := r.AllClimbs()
assert.Len(t, climbs, 6)
- assert.Equal(t, "3.6km-13.8km: 10.3km at 2.1% (44 pts - Cat 4)", climbs[0].String()) // TODO: Not steep enough
- assert.Equal(t, "34.7km-37.3km: 2.5km at 5.0% (62 pts - Cat 4)", climbs[1].String())
- assert.Equal(t, "41.9km-51.0km: 9.0km at 2.5% (55 pts - Cat 4)", climbs[2].String()) // TODO: Not steep enough
- assert.Equal(t, "57.6km-60.8km: 3.1km at 6.3% (124 pts - Cat 3)", climbs[3].String())
- assert.Equal(t, "71.7km-74.7km: 3.0km at 5.3% (84 pts - Cat 3)", climbs[4].String())
- assert.Equal(t, "77.4km-78.7km: 1.3km at 5.3% (37 pts - Cat 4)", climbs[5].String())
+ assert.Equal(t, "3.6km-13.8km: 10.3km at 2.1% (98 pts - Cat 3)", climbs[0].String()) // TODO: Not steep enough
+ assert.Equal(t, "34.7km-37.3km: 2.5km at 5.0% (67 pts - Cat 4)", climbs[1].String())
+ assert.Equal(t, "41.9km-51.0km: 9.0km at 2.5% (113 pts - Cat 3)", climbs[2].String()) // TODO: Not steep enough
+ assert.Equal(t, "57.6km-60.8km: 3.1km at 6.3% (139 pts - Cat 3)", climbs[3].String())
+ assert.Equal(t, "71.7km-74.7km: 3.0km at 5.3% (89 pts - Cat 3)", climbs[4].String())
+ assert.Equal(t, "77.4km-78.7km: 1.3km at 5.3% (39 pts - Cat 4)", climbs[5].String())
}
func TestMimetArbois20241229(t *testing.T) {
@@ -114,8 +114,8 @@ func TestMimetArbois20241229(t *testing.T) {
climbs := r.AllClimbs()
assert.Len(t, climbs, 2)
- assert.Equal(t, "3.9km-5.3km: 1.4km at 10.3% (153 pts - Cat 3)", climbs[0].String())
- assert.Equal(t, "14.4km-19.8km: 5.4km at 4.3% (98 pts - Cat 3)", climbs[1].String())
+ assert.Equal(t, "3.9km-5.3km: 1.4km at 10.3% (165 pts - Cat 3)", climbs[0].String())
+ assert.Equal(t, "14.4km-19.8km: 5.4km at 4.3% (113 pts - Cat 3)", climbs[1].String())
}
func TestMimetArbois20250104(t *testing.T) {
@@ -124,8 +124,8 @@ func TestMimetArbois20250104(t *testing.T) {
climbs := r.AllClimbs()
assert.Len(t, climbs, 2)
- assert.Equal(t, "4.5km-5.9km: 1.4km at 10.1% (148 pts - Cat 3)", climbs[0].String())
- assert.Equal(t, "12.0km-20.2km: 8.2km at 2.9% (67 pts - Cat 4)", climbs[1].String()) // TODO: Not steep enough
+ assert.Equal(t, "4.5km-5.9km: 1.4km at 10.1% (159 pts - Cat 3)", climbs[0].String())
+ assert.Equal(t, "12.0km-20.2km: 8.2km at 2.9% (148 pts - Cat 3)", climbs[1].String()) // TODO: Not steep enough
}
// https://www.strava.com/activities/9282265844
@@ -135,10 +135,10 @@ func TestAlpesVerdonTour20230617(t *testing.T) {
climbs := r.AllClimbs()
assert.Len(t, climbs, 6)
- assert.Equal(t, "0.7km-18.6km: 17.9km at 3.5% (217 pts - Cat 2)", climbs[0].String()) // TODO: Should split in 2 (10km at 4.5% is Cat 2)
- assert.Equal(t, "36.1km-41.6km: 5.4km at 5.8% (182 pts - Cat 2)", climbs[1].String())
+ assert.Equal(t, "0.7km-18.6km: 17.9km at 3.5% (364 pts - Cat 1)", climbs[0].String()) // TODO: Should split in 2 (10km at 4.5% is Cat 2)
+ assert.Equal(t, "36.1km-41.6km: 5.4km at 5.8% (193 pts - Cat 2)", climbs[1].String())
assert.Equal(t, "47.0km-48.2km: 1.2km at 6.1% (45 pts - Cat 4)", climbs[2].String())
- assert.Equal(t, "51.9km-54.7km: 2.8km at 4.0% (45 pts - Cat 4)", climbs[3].String())
- assert.Equal(t, "86.7km-92.8km: 6.1km at 2.7% (44 pts - Cat 4)", climbs[4].String())
- assert.Equal(t, "114.1km-114.7km: 0.6km at 14.3% (118 pts - Cat 3)", climbs[5].String())
+ assert.Equal(t, "51.9km-54.7km: 2.8km at 4.0% (48 pts - Cat 4)", climbs[3].String())
+ assert.Equal(t, "86.7km-92.8km: 6.1km at 2.7% (60 pts - Cat 4)", climbs[4].String())
+ assert.Equal(t, "114.1km-114.7km: 0.6km at 14.3% (119 pts - Cat 3)", climbs[5].String())
}
diff --git a/ride/ride.go b/ride/ride.go
index 3458483..152bca4 100644
--- a/ride/ride.go
+++ b/ride/ride.go
@@ -226,21 +226,6 @@ func metricValue(point gpx.GPXPoint, name string) (*float64, error) {
return &value, nil
}
-func (r *Ride) ScoreFromKm(start, end float64) float64 {
- i := 0
- j := 0
- for k, distance := range r.distances {
- if i == 0 && distance >= start*1000 {
- i = k
- }
- if j == 0 && distance >= end*1000 {
- j = k
- break
- }
- }
- return Score(*r, i, j)
-}
-
const CotacolAlgorithmVersion = "v1"
func Cotacol(ride Ride) float64 {
@@ -256,7 +241,7 @@ func difficultyScore(r Ride, startIndex, endIndex int) float64 {
if lastDistance <= startDistance {
return 0
}
- score := 0.0
+ cotacol := 0.0
i := startIndex
for start := startDistance; start < lastDistance; start += 100 {
end := math.Min(start+100, lastDistance)
@@ -270,10 +255,10 @@ func difficultyScore(r Ride, startIndex, endIndex int) float64 {
endElevation := interpolateElevation(r, i, end)
slope := (endElevation - startElevation) / (end - start)
if slope > 0 {
- score += (end - start) / 1000 * (slope * 100) * (slope * 100)
+ cotacol += (end - start) / 1000 * (slope * 100) * (slope * 100)
}
}
- return score
+ return cotacol
}
func interpolateElevation(r Ride, i int, distance float64) float64 {