aboutsummaryrefslogtreecommitdiff
path: root/climb/climb.go
blob: 2d764dd242200d951bbd7dcf41918b2f63b9caa6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

package climb

import (
	"fmt"
	"math"
)

const (
	ClimbDistanceMinimum  = 500
	ClimbSlopeMinimum     = 0.035
	detectionMinimumScore = 35
)

type Category int

const (
	CategoryNo Category = iota
	CategoryFour
	CategoryThree
	CategoryTwo
	CategoryOne
	CategoryHC
)

type categoryInfo struct {
	label    string
	minScore float64
}

var categories = map[Category]categoryInfo{
	CategoryNo:    {label: "NO", minScore: math.Inf(-1)},
	CategoryFour:  {label: "Cat 4", minScore: 35},
	CategoryThree: {label: "Cat 3", minScore: 80},
	CategoryTwo:   {label: "Cat 2", minScore: 180},
	CategoryOne:   {label: "Cat 1", minScore: 250},
	CategoryHC:    {label: "HC", minScore: 600},
}

func (c Category) String() string {
	return categories[c].label
}

func CategoryOf(cotacol float64) Category {
	best := CategoryNo
	bestMin := math.Inf(-1)
	found := false
	for category, info := range categories {
		if cotacol >= info.minScore && (!found || info.minScore > bestMin) {
			best = category
			bestMin = info.minScore
			found = true
		}
	}
	if !found {
		// NaN reaches no floor; preserve the original switch default (HC).
		return CategoryHC
	}
	return best
}

type Climb struct {
	Route
}

func (climb Climb) String() string {
	cotacol := climb.DifficultyScore()
	return fmt.Sprintf("%.1fkm at %.1f%% (%d pts - %s)", climb.Distances[len(climb.Distances)-1]/1000, Slope(climb.Distances, climb.Elevations)*100, int(cotacol), CategoryOf(cotacol))
}

func (climb Climb) DifficultyScore() float64 {
	return Cotacol(climb.Distances, climb.Elevations)
}

func (climb Climb) Top() (int, float64) {
	top := 0
	for i := 1; i < len(climb.Elevations); i++ {
		if climb.Elevations[i] > climb.Elevations[top] {
			top = i
		}
	}
	return top, climb.Elevations[top]
}

func Slope(distances, elevations []float64) float64 {
	end := len(distances) - 1
	return (elevations[end] - elevations[0]) / (distances[end] - distances[0])
}