aboutsummaryrefslogtreecommitdiff
path: root/ride/ride.go
diff options
context:
space:
mode:
Diffstat (limited to 'ride/ride.go')
-rw-r--r--ride/ride.go109
1 files changed, 14 insertions, 95 deletions
diff --git a/ride/ride.go b/ride/ride.go
index 7863878..8e8bf95 100644
--- a/ride/ride.go
+++ b/ride/ride.go
@@ -4,13 +4,13 @@ import (
"errors"
"fmt"
"io"
- "math"
"os"
"strconv"
"strings"
"time"
"github.com/jftuga/geodist"
+ "github.com/martinlehoux/biking_home/climb"
"github.com/martinlehoux/kagamigo/kcore"
"github.com/tkrajina/gpxgo/gpx"
)
@@ -20,9 +20,7 @@ const garminTrackPointExtensionNamespace = "http://www.garmin.com/xmlschemas/Tra
var ErrNoTrackPoints = errors.New("ride has no track points")
type Ride struct {
- distances []float64
- elevations []float64
- coords []geodist.Coord
+ climb.Route
timestamps []time.Time
heartRates []float64
cadences []float64
@@ -31,26 +29,20 @@ type Ride struct {
func (r *Ride) check() {
kcore.Assert(r.Len() > 0, "no points in ride")
- kcore.Assert(len(r.distances) == len(r.elevations), "ride columns have different lengths")
- kcore.Assert(len(r.distances) == len(r.coords), "ride columns have different lengths")
- kcore.Assert(len(r.distances) == len(r.timestamps), "ride columns have different lengths")
+ kcore.Assert(len(r.Distances) == len(r.Elevations), "ride columns have different lengths")
+ kcore.Assert(len(r.Distances) == len(r.Coords), "ride columns have different lengths")
+ kcore.Assert(len(r.Distances) == len(r.timestamps), "ride columns have different lengths")
for _, column := range [][]float64{r.heartRates, r.cadences, r.powers} {
if len(column) > 0 {
- kcore.Assert(len(r.distances) == len(column), "ride columns have different lengths")
+ kcore.Assert(len(r.Distances) == len(column), "ride columns have different lengths")
}
}
}
func (r Ride) Len() int {
- return len(r.distances)
+ return len(r.Distances)
}
-func (r Ride) Distances() []float64 { return r.distances }
-
-func (r Ride) Elevations() []float64 { return r.elevations }
-
-func (r Ride) Coords() []geodist.Coord { return r.coords }
-
func (r Ride) Timestamps() []time.Time { return r.timestamps }
func (r Ride) Powers() ([]float64, bool) { return optionalComponent(r.powers) }
@@ -170,9 +162,11 @@ func FromColumnsWithMetrics(distances []float64, elevations []float64, coords []
func fromColumns(distances []float64, elevations []float64, coords []geodist.Coord, timestamps []time.Time, heartRates, cadences, powers []float64) Ride {
ride := Ride{
- distances: distances,
- elevations: elevations,
- coords: coords,
+ Route: climb.Route{
+ Distances: distances,
+ Elevations: elevations,
+ Coords: coords,
+ },
timestamps: timestamps,
heartRates: heartRates,
cadences: cadences,
@@ -229,81 +223,6 @@ func metricValue(point gpx.GPXPoint, name string) (*float64, error) {
return &value, nil
}
-const CotacolAlgorithmVersion = "v1"
-
-// cotacolSegmentLengthM is the fixed length of the elevation segments the
-// Cotacol score is computed over.
-const cotacolSegmentLengthM = 100
-
-func Cotacol(ride Ride) float64 {
- return difficultyScore(ride.distances, ride.elevations, 0, ride.Len()-1)
-}
-
-func difficultyScore(distances, elevations []float64, startIndex, endIndex int) float64 {
- if endIndex-startIndex < 1 {
- return 0
- }
- startDistance := distances[startIndex]
- lastDistance := distances[endIndex]
- if lastDistance <= startDistance {
- return 0
- }
- cotacol := 0.0
- i := startIndex
- for start := startDistance; start < lastDistance; start += cotacolSegmentLengthM {
- end := math.Min(start+cotacolSegmentLengthM, lastDistance)
- for i < endIndex && distances[i+1] <= start {
- i++
- }
- startElevation := interpolateElevation(distances, elevations, i, start)
- for i < endIndex && distances[i+1] < end {
- i++
- }
- endElevation := interpolateElevation(distances, elevations, i, end)
- slope := (endElevation - startElevation) / (end - start)
- if slope > 0 {
- cotacol += (end - start) / 1000 * (slope * 100) * (slope * 100)
- }
- }
- return cotacol
-}
-
-func interpolateElevation(distances, elevations []float64, i int, distance float64) float64 {
- if i+1 >= len(distances) {
- return elevations[i]
- }
- startDistance := distances[i]
- endDistance := distances[i+1]
- t := (distance - startDistance) / (endDistance - startDistance)
- return elevations[i] + t*(elevations[i+1]-elevations[i])
-}
-
-func (r *Ride) ClimbFromDist(startDist, endDist float64) Climb {
- start, end := 0, 0
- for i, distance := range r.distances {
- if start == 0 && distance >= startDist {
- start = i
- }
- if end == 0 && distance >= endDist {
- end = i
- break
- }
- }
- return r.climbFrom(start, end)
-}
-
-func (r *Ride) ClimbFromIndexes(startIndex, endIndex int) Climb {
- kcore.Assert(startIndex >= 0 && endIndex < r.Len() && startIndex < endIndex, "invalid climb indexes")
- return r.climbFrom(startIndex, endIndex)
-}
-
-func (r *Ride) climbFrom(start, end int) Climb {
- return Climb{
- distances: r.distances,
- elevations: r.elevations,
- coords: r.coords,
- timestamps: r.timestamps,
- rideStart: start,
- rideEnd: end,
- }
+func (ride Ride) Cotacol() float64 {
+ return climb.Cotacol(ride.Distances, ride.Elevations)
}