summaryrefslogtreecommitdiff
path: root/rideanalysis/analysis.go
blob: 3d34855bdcc195971f74e69d60f39c74faa1c9f1 (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
88
89
90
91
92
93
94
95
package rideanalysis

import (
	"math"

	"github.com/jftuga/geodist"
	"github.com/martinlehoux/biking_home/mountain_pass"
	"github.com/martinlehoux/biking_home/official_climb"
	"github.com/martinlehoux/biking_home/ride"
)

type Result struct {
	Climbs    []AnalyzedClimb
	Crossings []mountain_pass.Crossing
}

type AnalyzedClimb struct {
	Segment         ride.Climb
	Name            string
	Score           float64
	Category        string
	DistanceM       float64
	SlopePercent    float64
	Cotacol         float64
	OfficialClimbID int64
	OfficialName    string
}

func Analyze(parsed ride.Ride, passes []mountain_pass.MountainPass, officialClimbs []official_climb.OfficialClimb, matchPolicy official_climb.MatchPolicy) Result {
	result := Result{
		Climbs:    make([]AnalyzedClimb, 0),
		Crossings: make([]mountain_pass.Crossing, 0),
	}
	for _, detectedClimb := range parsed.AllClimbs() {
		displayClimb := detectedClimb
		if matchedPass, ok := mountain_pass.MatchClimb(displayClimb, passes, 300, 50); ok {
			displayClimb.Name = matchedPass.Name
		}
		matchedOfficial, officialFound := official_climb.MatchClimb(detectedClimb, officialClimbs, matchPolicy)
		if officialFound {
			startIndex := nearestCoordIndex(parsed, matchedOfficial.StartCoord)
			endIndex := nearestCoordIndex(parsed, matchedOfficial.EndCoord)
			if startIndex < endIndex {
				displayClimb = parsed.ClimbFromIndexes(startIndex, endIndex)
				displayClimb.Name = matchedOfficial.Name
			} else {
				officialFound = false
				matchedOfficial = official_climb.OfficialClimb{}
			}
		}
		score := displayClimb.Score()
		result.Climbs = append(result.Climbs, AnalyzedClimb{
			Segment:         displayClimb,
			Name:            displayClimb.Name,
			Score:           score,
			Category:        ride.Category(score),
			DistanceM:       displayClimb.EndDistanceM() - displayClimb.StartDistanceM(),
			SlopePercent:    ride.Slope(parsed, displayClimb.StartIndex(), displayClimb.EndIndex()) * 100,
			Cotacol:         displayClimb.DifficultyScore(),
			OfficialClimbID: officialClimbID(matchedOfficial, officialFound),
			OfficialName:    officialClimbName(matchedOfficial, officialFound),
		})
	}
	for _, crossing := range mountain_pass.DetectCrossings(parsed, passes, 100, 25) {
		result.Crossings = append(result.Crossings, crossing)
	}
	return result
}

func nearestCoordIndex(parsed ride.Ride, target geodist.Coord) int {
	bestIndex := 0
	bestDistance := math.Inf(1)
	for index := 0; index < parsed.Len(); index++ {
		_, distanceKm := geodist.HaversineDistance(parsed.Coord(index), target)
		if distanceKm < bestDistance {
			bestDistance = distanceKm
			bestIndex = index
		}
	}
	return bestIndex
}

func officialClimbID(climb official_climb.OfficialClimb, found bool) int64 {
	if !found {
		return 0
	}
	return climb.ID
}

func officialClimbName(climb official_climb.OfficialClimb, found bool) string {
	if !found {
		return ""
	}
	return climb.Name
}