summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--go.mod10
-rw-r--r--go.sum46
-rw-r--r--ride/.snapshots/TestRideClimbLaCride4
-rw-r--r--ride/climb.go31
-rw-r--r--ride/index.go36
-rw-r--r--ride/index_test.go36
-rw-r--r--ride/ride.go27
7 files changed, 179 insertions, 11 deletions
diff --git a/go.mod b/go.mod
index ac608b8..8eb0702 100644
--- a/go.mod
+++ b/go.mod
@@ -12,17 +12,27 @@ require (
)
require (
+ codeberg.org/go-fonts/liberation v0.5.0 // indirect
+ codeberg.org/go-latex/latex v0.1.0 // indirect
+ codeberg.org/go-pdf/fpdf v0.10.0 // indirect
+ git.sr.ht/~sbinet/gg v0.6.0 // indirect
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/a-h/templ v0.2.747 // indirect
+ github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b // indirect
+ github.com/bradleyjkemp/cupaloy v1.3.0 // indirect
+ github.com/campoy/embedmd v1.0.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gofrs/uuid v4.4.0+incompatible // indirect
+ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/kataras/i18n v0.0.8 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
+ golang.org/x/image v0.25.0 // indirect
golang.org/x/net v0.27.0 // indirect
golang.org/x/text v0.23.0 // indirect
+ gonum.org/v1/plot v0.16.0 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
diff --git a/go.sum b/go.sum
index 103752b..19e7ad1 100644
--- a/go.sum
+++ b/go.sum
@@ -1,19 +1,39 @@
+codeberg.org/go-fonts/liberation v0.5.0 h1:SsKoMO1v1OZmzkG2DY+7ZkCL9U+rrWI09niOLfQ5Bo0=
+codeberg.org/go-fonts/liberation v0.5.0/go.mod h1:zS/2e1354/mJ4pGzIIaEtm/59VFCFnYC7YV6YdGl5GU=
+codeberg.org/go-latex/latex v0.1.0 h1:hoGO86rIbWVyjtlDLzCqZPjNykpWQ9YuTZqAzPcfL3c=
+codeberg.org/go-latex/latex v0.1.0/go.mod h1:LA0q/AyWIYrqVd+A9Upkgsb+IqPcmSTKc9Dny04MHMw=
+codeberg.org/go-pdf/fpdf v0.10.0 h1:u+w669foDDx5Ds43mpiiayp40Ov6sZalgcPMDBcZRd4=
+codeberg.org/go-pdf/fpdf v0.10.0/go.mod h1:Y0DGRAdZ0OmnZPvjbMp/1bYxmIPxm0ws4tfoPOc4LjU=
+git.sr.ht/~sbinet/gg v0.6.0 h1:RIzgkizAk+9r7uPzf/VfbJHBMKUr0F5hRFxTUGMnt38=
+git.sr.ht/~sbinet/gg v0.6.0/go.mod h1:uucygbfC9wVPQIfrmwM2et0imr8L7KQWywX0xpFMm94=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/a-h/templ v0.2.747 h1:D0dQ2lxC3W7Dxl6fxQ/1zZHBQslSkTSvl5FxP/CfdKg=
github.com/a-h/templ v0.2.747/go.mod h1:69ObQIbrcuwPCU32ohNaWce3Cb7qM5GMiqN1K+2yop4=
+github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
+github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
+github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyRiyQj/Ud48djTMtMebDqepE95rw=
+github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM=
+github.com/bradleyjkemp/cupaloy v1.3.0 h1:UJ0YJuhkMXEQcaoQNSCmK8og6GEVW/eDhAZuizvcpOY=
+github.com/bradleyjkemp/cupaloy v1.3.0/go.mod h1:Au1Xw1sgaJ5iSFktEhYsS0dbQiS1B0/XMXl+42y9Ilk=
+github.com/campoy/embedmd v1.0.0 h1:V4kI2qTJJLf4J29RzI/MAt2c3Bl4dQSYPuflzwFH2hY=
+github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
+github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/jftuga/geodist v1.0.0 h1:WzDVdHJGyCF2lDIbTLCUJxchlvnBEQyORSB2AN9/Mns=
github.com/jftuga/geodist v1.0.0/go.mod h1:BohEDxpZ8S5ADAxW/9EKPSKWOVl0+3wHENIT40m4UO4=
github.com/kataras/i18n v0.0.8 h1:thiDRqq4fN2sQOK5CwR5Yh1yT7swP6B3wnadILhqjhk=
github.com/kataras/i18n v0.0.8/go.mod h1:M/yRAqQ3Y7z2oSpotxG/+nPrgsJLas6t0kEJfIJk98E=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -30,18 +50,43 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tkrajina/gpxgo v1.4.0 h1:cSD5uSwy3VZuNFieTEZLyRnuIwhonQEkGPkPGW4XNag=
github.com/tkrajina/gpxgo v1.4.0/go.mod h1:BXSMfUAvKiEhMEXAFM2NvNsbjsSvp394mOvdcNjettg=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
+golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ=
+golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gonum.org/v1/plot v0.16.0 h1:dK28Qx/Ky4VmPUN/2zeW0ELyM6ucDnBAj5yun7M9n1g=
+gonum.org/v1/plot v0.16.0/go.mod h1:Xz6U1yDMi6Ni6aaXILqmVIb6Vro8E+K7Q/GeeH+Pn0c=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -50,3 +95,4 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
diff --git a/ride/.snapshots/TestRideClimbLaCride b/ride/.snapshots/TestRideClimbLaCride
new file mode 100644
index 0000000..386c553
--- /dev/null
+++ b/ride/.snapshots/TestRideClimbLaCride
@@ -0,0 +1,4 @@
+([]string) (len=2) {
+ (string) (len=69) "Found similar climbs to 17.1km-18.8km: 1.7km at 4.8% (38 pts - Cat 4)",
+ (string) (len=47) " 17.2km-18.8km: 1.6km at 4.9% (38 pts - Cat 4)"
+}
diff --git a/ride/climb.go b/ride/climb.go
index d2d5d0e..0a9994d 100644
--- a/ride/climb.go
+++ b/ride/climb.go
@@ -14,9 +14,20 @@ import (
const ClimbDistanceMinimum = 500
type Climb struct {
- start int
- end int
- points []Point
+ rideStart int
+ rideEnd int
+ points []Point
+}
+
+func (climb Climb) Duration() time.Duration {
+ return climb.points[len(climb.points)-1].Timestamp.Sub(climb.points[0].Timestamp)
+}
+func (climb Climb) Start() Point {
+ return climb.points[0]
+}
+
+func (climb Climb) End() Point {
+ return climb.points[len(climb.points)-1]
}
func (climb Climb) String() string {
@@ -92,9 +103,9 @@ func bestClimbBetween(points []Point, start int, end int) Climb {
bestScore = score
}
}
- climb := Climb{start: bestStart, end: bestEnd, points: points[bestStart : bestEnd+1]}
+ kcore.Assert(bestStart < bestEnd, "empty climb")
+ climb := Climb{rideStart: bestStart, rideEnd: bestEnd, points: points[bestStart : bestEnd+1]}
- kcore.Assert(climb.start < climb.end, "empty climb")
return climb
}
@@ -115,12 +126,12 @@ func climbsBetween(points []Point, start int, end int) []Climb {
return climbsBetween(points, start+1, end)
}
climb := bestClimbBetween(points, start, highest)
- if Score(points, climb.start, climb.end) >= 35 && points[climb.end].DistanceM-points[climb.start].DistanceM >= ClimbDistanceMinimum {
- slog.Debug("Found climb between", slog.Int("start", int(points[climb.start].DistanceM)), slog.Int("end", int(points[climb.end].DistanceM)))
+ if Score(points, climb.rideStart, climb.rideEnd) >= 35 && points[climb.rideEnd].DistanceM-points[climb.rideStart].DistanceM >= ClimbDistanceMinimum {
+ slog.Debug("Found climb between", slog.Int("start", int(points[climb.rideStart].DistanceM)), slog.Int("end", int(points[climb.rideEnd].DistanceM)))
climbs = append(climbs, climb)
}
- climbs = append(climbs, climbsBetween(points, start, climb.start)...)
- climbs = append(climbs, climbsBetween(points, climb.end, end)...)
+ climbs = append(climbs, climbsBetween(points, start, climb.rideStart)...)
+ climbs = append(climbs, climbsBetween(points, climb.rideEnd, end)...)
return climbs
}
@@ -131,4 +142,4 @@ func (ride *Ride) AllClimbs() []Climb {
return climbs
}
-func climbCmpStart(a, b Climb) int { return a.start - b.start }
+func climbCmpStart(a, b Climb) int { return a.rideStart - b.rideStart }
diff --git a/ride/index.go b/ride/index.go
new file mode 100644
index 0000000..18e2ac8
--- /dev/null
+++ b/ride/index.go
@@ -0,0 +1,36 @@
+package ride
+
+import "github.com/jftuga/geodist"
+
+type RideClimbsIndex interface {
+ Insert(ride Ride)
+ Similar(ride Ride, climb Climb) []Climb
+}
+
+type FlatRideClimbsIndex struct {
+ sensibility float64
+ climbs []Climb
+}
+
+func NewFlatRideClimbsIndex(sensibility float64) *FlatRideClimbsIndex {
+ return &FlatRideClimbsIndex{
+ climbs: make([]Climb, 0),
+ sensibility: sensibility,
+ }
+}
+
+func (index *FlatRideClimbsIndex) Insert(ride Ride) {
+ index.climbs = append(index.climbs, ride.AllClimbs()...)
+}
+
+func (index *FlatRideClimbsIndex) Similar(ride Ride, climb Climb) []Climb {
+ results := make([]Climb, 0)
+ for _, c := range index.climbs {
+ _, startDistance := geodist.HaversineDistance(c.Start().Coord, climb.Start().Coord)
+ _, endDistance := geodist.HaversineDistance(c.End().Coord, climb.End().Coord)
+ if startDistance <= index.sensibility/1000 && endDistance <= index.sensibility/1000 {
+ results = append(results, c)
+ }
+ }
+ return results
+}
diff --git a/ride/index_test.go b/ride/index_test.go
new file mode 100644
index 0000000..e95f030
--- /dev/null
+++ b/ride/index_test.go
@@ -0,0 +1,36 @@
+package ride_test
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/bradleyjkemp/cupaloy"
+ "github.com/martinlehoux/biking_home/ride"
+ "github.com/stretchr/testify/assert"
+ "github.com/tkrajina/gpxgo/gpx"
+)
+
+func TestRideClimbLaCride(t *testing.T) {
+ gpxContent, err := gpx.ParseFile("../examples/activity_18043356988.gpx")
+ assert.NoError(t, err)
+ ride19Jan := ride.FromGPX(gpxContent)
+ gpxContent, err = gpx.ParseFile("../examples/activity_18679866717.gpx")
+ assert.NoError(t, err)
+ ride30Mar := ride.FromGPX(gpxContent)
+ gpxContent, err = gpx.ParseFile("../examples/activity_18880605641.gpx")
+ assert.NoError(t, err)
+ ride20Apr := ride.FromGPX(gpxContent)
+ index := ride.NewFlatRideClimbsIndex(30)
+ index.Insert(ride19Jan)
+ index.Insert(ride30Mar)
+ logs := []string{}
+ for _, climb := range ride20Apr.AllClimbs() {
+ if len(index.Similar(ride20Apr, climb)) > 0 {
+ logs = append(logs, fmt.Sprintf("Found similar climbs to %s", climb))
+ for _, similar := range index.Similar(ride20Apr, climb) {
+ logs = append(logs, fmt.Sprintf(" %s", similar))
+ }
+ }
+ }
+ cupaloy.SnapshotT(t, logs)
+}
diff --git a/ride/ride.go b/ride/ride.go
index c8ecacc..fb774e4 100644
--- a/ride/ride.go
+++ b/ride/ride.go
@@ -4,6 +4,9 @@ import (
"github.com/jftuga/geodist"
"github.com/martinlehoux/kagamigo/kcore"
"github.com/tkrajina/gpxgo/gpx"
+ "gonum.org/v1/plot"
+ "gonum.org/v1/plot/plotter"
+ "gonum.org/v1/plot/vg"
)
type Ride struct {
@@ -63,5 +66,27 @@ func (r *Ride) ClimbFromDist(startDist, endDist float64) Climb {
break
}
}
- return Climb{start: start, end: end, points: r.points[start : end+1]}
+ return Climb{rideStart: start, rideEnd: end, points: r.points[start : end+1]}
+}
+
+func Plot(r *Ride, outputFile string) {
+ r.check()
+
+ pts := make(plotter.XYs, len(r.points))
+ for i, p := range r.points {
+ pts[i].X = p.DistanceM / 1000 // Convert distance to kilometers
+ pts[i].Y = p.ElevationM
+ }
+
+ p := plot.New()
+ p.Title.Text = "Ride Elevation Profile"
+ p.X.Label.Text = "Distance (km)"
+ p.Y.Label.Text = "Elevation (m)"
+
+ line, err := plotter.NewLine(pts)
+ kcore.Expect(err, "failed to create line plot")
+ p.Add(line)
+
+ err = p.Save(10*vg.Inch, 4*vg.Inch, outputFile)
+ kcore.Expect(err, "failed to save plot")
}