summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md13
-rw-r--r--chart/chart.go81
-rw-r--r--cli/cli.go27
-rw-r--r--go.mod9
-rw-r--r--go.sum46
-rw-r--r--ride/ride.go89
6 files changed, 3 insertions, 262 deletions
diff --git a/README.md b/README.md
index aa55cca..f5f57df 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ A Go toolkit for analyzing cycling rides from GPX exports: parse rides, detect c
- **Similar-climb matching** — finds the same climb across rides by matching start/end coordinates, so times can be compared
- **Mountain pass download** — imports French mountain passes from centcols.org into a SQLite database
- **Pass crossing detection** — enriches passes with OSM coordinates, flags which passes a ride crosses, and names each climb after the pass it tops (e.g. "Col de Castellaras")
-- **Plots** — renders elevation and score-profile charts as PNG
+- **Interactive ride profiles** — explores elevation, climbs, and mountain-pass crossings in the web ride detail view
- **Web ride library** — starts a local web server by default, imports Strava rides over a date range, stores metadata in SQLite, and keeps their GPX files on disk
- **Materialized ride values** — computes Cotacol on import, stores its algorithm version in SQLite, and refreshes all computed values with `-backfill`
- **Strava stream metrics** — preserves heart rate, cadence, and power in Garmin-compatible GPX files and in-memory ride columns
@@ -49,9 +49,6 @@ mise watch dev-watch --restart --exts go,templ --ignore '*_templ.go'
# Backfill OSM coordinates onto passes (needs -extract-osm run first)
./biking_home -enrich
-# Render an elevation chart with climb highlights and pass markers
-./biking_home -chart examples/2023-06-17.AlpesVerdonTour.gpx
-
```
## Options / Configuration
@@ -64,7 +61,6 @@ mise watch dev-watch --restart --exts go,templ --ignore '*_templ.go'
| `-extract-osm` | Extract mountain passes from a France OSM PBF | `""` |
| `-enrich` | Backfill OSM coordinates onto passes | `false` |
| `-backfill` | Recompute and persist all ride computed values | `false` |
-| `-chart` | Render an elevation chart (climbs + passes) for a GPX file | `""` |
Configuration is stored in `config.yaml`. Start from `config.example.yaml` and set the Strava credentials before launching the server.
@@ -82,7 +78,7 @@ Standard apps are rate-limited to 100 calls per 15 minutes and 1,000 per day.
## Architecture
-- Go 1.23; SQLite via `mattn/go-sqlite3`; charts via `gonum.org/v1/plot`
+- Go 1.23; SQLite via `mattn/go-sqlite3`
- `ride` — GPX parsing, climb detection, difficulty scores (KOM + Cotacol), similarity index
- `mountain_pass` — centcols.org department CSV download into SQLite, with disk caching and retries
- `osmpass` — OSM PBF extraction (`mountain_pass=yes` nodes) and pass coordinate enrichment
@@ -90,8 +86,7 @@ Standard apps are rate-limited to 100 calls per 15 minutes and 1,000 per day.
- `rides` — SQLite persistence for imported ride metadata and versioned computed values
- `config` — typed YAML configuration and atomic persistence
- `web` — HTTP server, OAuth callback, sync orchestration, and templ pages
-- `cli` — legacy mountain-pass, OSM, and chart command handlers
-- `chart` — elevation chart rendering
+- `cli` — legacy mountain-pass and OSM command handlers
- **Notable choices** — the difficulty score follows the Cotacol method: the ride is split into fixed 100 m segments and each scores `distance_km × slope²`, so steep sections weigh exponentially more than long flat ones
```mermaid
@@ -108,14 +103,12 @@ flowchart TB
config["config (YAML)"]
web["web (HTTP + templ)"]
cli["cli (legacy commands)"]
- chart["chart (renderer)"]
main --> cli
cli --> ride
cli --> mpass
cli --> osmpass
cli --> web
- cli --> chart
mpass --> ride
web --> strava
web --> rides
diff --git a/chart/chart.go b/chart/chart.go
deleted file mode 100644
index 7f5a4c7..0000000
--- a/chart/chart.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package chart
-
-import (
- "fmt"
- "image/color"
-
- "github.com/martinlehoux/biking_home/mountain_pass"
- "github.com/martinlehoux/biking_home/ride"
- "github.com/martinlehoux/kagamigo/kcore"
- "gonum.org/v1/plot"
- "gonum.org/v1/plot/plotter"
- "gonum.org/v1/plot/vg"
-)
-
-func Render(r ride.Ride, climbs []ride.Climb, crossings []mountain_pass.Crossing, output string) {
- p := plot.New()
- p.Title.Text = "Ride profile"
- p.X.Label.Text = "Distance (km)"
- p.Y.Label.Text = "Elevation (m)"
-
- maxElev := 0.0
- elevation := make(plotter.XYs, r.Len())
- for i := 0; i < r.Len(); i++ {
- elevation[i].X = r.DistanceM(i) / 1000
- elevation[i].Y = r.ElevationM(i)
- if r.ElevationM(i) > maxElev {
- maxElev = r.ElevationM(i)
- }
- }
-
- for _, climb := range climbs {
- x0 := climb.StartDistanceM() / 1000
- x1 := climb.EndDistanceM() / 1000
- fill, err := plotter.NewPolygon(plotter.XYs{
- {X: x0, Y: 0},
- {X: x1, Y: 0},
- {X: x1, Y: maxElev},
- {X: x0, Y: maxElev},
- })
- kcore.Expect(err, "failed to create climb band")
- fill.Color = color.RGBA{R: 230, G: 160, B: 0, A: 70}
- fill.LineStyle.Width = 0
- p.Add(fill)
- p.Add(plotLabels(plotter.XY{X: (x0 + x1) / 2, Y: climb.TopElevationM() * 1.01}, climbLabel(climb)))
- }
-
- for _, crossing := range crossings {
- x := crossing.RideDistanceM / 1000
- line, err := plotter.NewLine(plotter.XYs{
- {X: x, Y: 0},
- {X: x, Y: maxElev},
- })
- kcore.Expect(err, "failed to create pass marker")
- line.LineStyle.Dashes = []vg.Length{vg.Points(4), vg.Points(2)}
- p.Add(line)
- p.Add(plotLabels(plotter.XY{X: x, Y: maxElev * 0.96},
- fmt.Sprintf("%s %dm", crossing.Pass.Name, crossing.Pass.Elevation)))
- }
-
- elevLine, err := plotter.NewLine(elevation)
- kcore.Expect(err, "failed to create elevation line")
- p.Add(elevLine)
-
- kcore.Expect(p.Save(10*vg.Inch, 4*vg.Inch, output), "failed to save chart")
-}
-
-func climbLabel(climb ride.Climb) string {
- if climb.Name != "" {
- return climb.Name
- }
- return fmt.Sprintf("%s %d", ride.Category(climb.Score()), int(climb.Score()))
-}
-
-func plotLabels(xy plotter.XY, label string) *plotter.Labels {
- labels, err := plotter.NewLabels(plotter.XYLabels{
- XYs: plotter.XYs{xy},
- Labels: []string{label},
- })
- kcore.Expect(err, "failed to create labels")
- return labels
-}
diff --git a/cli/cli.go b/cli/cli.go
index 1d1b0f3..eb96356 100644
--- a/cli/cli.go
+++ b/cli/cli.go
@@ -5,15 +5,11 @@ import (
"database/sql"
"flag"
"log/slog"
- "path/filepath"
- "strings"
"time"
- "github.com/martinlehoux/biking_home/chart"
"github.com/martinlehoux/biking_home/config"
"github.com/martinlehoux/biking_home/mountain_pass"
"github.com/martinlehoux/biking_home/osmpass"
- "github.com/martinlehoux/biking_home/ride"
"github.com/martinlehoux/biking_home/rides"
"github.com/martinlehoux/biking_home/web"
"github.com/martinlehoux/kagamigo/kcore"
@@ -27,8 +23,6 @@ var (
extractOSM = flag.String("extract-osm", "", "extract mountain passes from an OSM PBF file into the database")
enrich = flag.Bool("enrich", false, "backfill mountain pass coordinates from OSM data")
backfill = flag.Bool("backfill", false, "recompute all stored ride values")
- chartFile = flag.String("chart", "", "render a climb/pass chart for a GPX file")
- parser = ride.GPXRideParser{}
)
func Run(db *sql.DB, configPath string, appConfig config.Config) {
@@ -52,8 +46,6 @@ func Run(db *sql.DB, configPath string, appConfig config.Config) {
count, err := rides.Backfill(db)
kcore.Expect(err, "failed to backfill ride values")
slog.Info("Backfilled ride values", "rides", count)
- case *chartFile != "":
- runChart(db, *chartFile)
default:
runServer(db, configPath, appConfig)
}
@@ -64,22 +56,3 @@ func runServer(db *sql.DB, configPath string, appConfig config.Config) {
slog.Info("Starting web server", "address", appConfig.Server.PublicURL)
kcore.Expect(server.ListenAndServe(appConfig.Server.Address), "web server stopped")
}
-
-func runChart(db *sql.DB, filename string) {
- r, err := ride.ParseFile(parser, filename)
- kcore.Expect(err, "failed to parse ride")
- passes, err := mountain_pass.LoadMountainPasses(db)
- kcore.Expect(err, "failed to load mountain passes")
-
- climbs := r.AllClimbs()
- for i := range climbs {
- if matched, ok := mountain_pass.MatchClimb(climbs[i], passes, 300, 50); ok {
- climbs[i].Name = matched.Name
- }
- }
- crossings := mountain_pass.DetectCrossings(r, passes, 100, 25)
-
- output := strings.TrimSuffix(filename, filepath.Ext(filename)) + "-chart.png"
- chart.Render(r, climbs, crossings, output)
- slog.Info("Chart written", "file", output)
-}
diff --git a/go.mod b/go.mod
index f066362..54400c7 100644
--- a/go.mod
+++ b/go.mod
@@ -13,26 +13,19 @@ require (
github.com/stretchr/testify v1.10.0
github.com/tkrajina/gpxgo v1.4.0
golang.org/x/text v0.34.0
- gonum.org/v1/plot v0.16.0
gopkg.in/yaml.v3 v3.0.1
)
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/ClickHouse/clickhouse-go v1.5.4 // indirect
github.com/DataDog/czlib v0.0.0-20240814115052-86a9592b3985 // indirect
github.com/PuerkitoBio/goquery v1.8.1 // indirect
github.com/a-h/parse v0.0.0-20240121214402-3caf7543159a // indirect
github.com/a-h/protocol v0.0.0-20240704131721-1e461c188041 // indirect
- github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b // indirect
github.com/amacneil/dbmate v1.16.2 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/andybalholm/cascadia v1.3.1 // indirect
- github.com/campoy/embedmd v1.0.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cli/browser v1.3.0 // indirect
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 // indirect
@@ -42,7 +35,6 @@ require (
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/gofrs/uuid v4.4.0+incompatible // indirect
- github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/joho/godotenv v1.4.0 // indirect
github.com/kataras/i18n v0.0.8 // indirect
github.com/kr/pretty v0.1.0 // indirect
@@ -67,7 +59,6 @@ require (
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
- golang.org/x/image v0.25.0 // indirect
golang.org/x/mod v0.32.0 // indirect
golang.org/x/net v0.51.0 // indirect
golang.org/x/sync v0.19.0 // indirect
diff --git a/go.sum b/go.sum
index 0f5b5dc..89c2148 100644
--- a/go.sum
+++ b/go.sum
@@ -1,18 +1,3 @@
-codeberg.org/go-fonts/dejavu v0.4.0 h1:2yn58Vkh4CFK3ipacWUAIE3XVBGNa0y1bc95Bmfx91I=
-codeberg.org/go-fonts/dejavu v0.4.0/go.mod h1:abni088lmhQJvso2Lsb7azCKzwkfcnttl6tL1UTWKzg=
-codeberg.org/go-fonts/latin-modern v0.4.0 h1:vkRCc1y3whKA7iL9Ep0fSGVuJfqjix0ica9UflHORO8=
-codeberg.org/go-fonts/latin-modern v0.4.0/go.mod h1:BF68mZznJ9QHn+hic9ks2DaFl4sR5YhfM6xTYaP9vNw=
-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/cmpimg v0.1.0 h1:E0zPRk2muWuCqSKSVZIWsgtU9pjsw3eKHi8VmQeScxo=
-git.sr.ht/~sbinet/cmpimg v0.1.0/go.mod h1:FU12psLbF4TfNXkKH2ZZQ29crIqoiqTZmeQ7dkp/pxE=
-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/ClickHouse/clickhouse-go v1.5.4 h1:cKjXeYLNWVJIx2J1K6H2CqyRmfwVJVY1OV1coaaFcI0=
@@ -27,10 +12,6 @@ github.com/a-h/protocol v0.0.0-20240704131721-1e461c188041 h1:2enlC41iOwWklx9ZUq
github.com/a-h/protocol v0.0.0-20240704131721-1e461c188041/go.mod h1:Gm0KywveHnkiIhqFSMZglXwWZRQICg3KDWLYdglv/d8=
github.com/a-h/templ v0.2.793 h1:Io+/ocnfGWYO4VHdR0zBbf39PQlnzVCVVD+wEEs6/qY=
github.com/a-h/templ v0.2.793/go.mod h1:lq48JXoUvuQrU0VThrK31yFwdRjTCnIE5bcPCM9IP1w=
-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/amacneil/dbmate v1.16.2 h1:ovhzYRR2JT5EZbISNtg7MZmLM51ZrHLKoEKMPhiFz5E=
github.com/amacneil/dbmate v1.16.2/go.mod h1:d+2u+wE7GpLepbKxi231FXoi7thXuI1AND5CRG18RcI=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
@@ -41,8 +22,6 @@ github.com/bkaradzic/go-lz4 v1.0.0 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAK
github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
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/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM=
@@ -65,8 +44,6 @@ github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
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/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@@ -79,7 +56,6 @@ github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
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=
@@ -136,7 +112,6 @@ github.com/urfave/cli/v2 v2.24.2 h1:q1VA+ofZ8SWfEKB9xXHUD4QZaeI9e+ItEqSbfH2JBXk=
github.com/urfave/cli/v2 v2.24.2/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
-github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zenizh/go-capturer v0.0.0-20211219060012-52ea6c8fed04 h1:qXafrlZL1WsJW5OokjraLLRURHiw0OzKHD/RNdspp4w=
github.com/zenizh/go-capturer v0.0.0-20211219060012-52ea6c8fed04/go.mod h1:FiwNQxz6hGoNFBC4nIx+CxZhI3nne5RmIOlT/MXcSD4=
@@ -155,20 +130,13 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
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/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
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/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
-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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@@ -177,15 +145,11 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
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/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
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/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -209,18 +173,11 @@ golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
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/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
-gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
-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=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
@@ -233,6 +190,3 @@ 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=
-rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=
-rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
diff --git a/ride/ride.go b/ride/ride.go
index 52f95b8..5471535 100644
--- a/ride/ride.go
+++ b/ride/ride.go
@@ -13,10 +13,6 @@ 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/plotutil"
- "gonum.org/v1/plot/vg"
)
const garminTrackPointExtensionNamespace = "http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
@@ -303,88 +299,3 @@ func (r *Ride) ClimbFromDist(startDist, endDist float64) Climb {
}
return Climb{ride: *r, rideStart: start, rideEnd: end}
}
-
-func Plot(r *Ride, outputFile string) {
- r.check()
-
- pts := make(plotter.XYs, r.Len())
- for i := 0; i < r.Len(); i++ {
- pts[i].X = r.DistanceM(i) / 1000 // Convert distance to kilometers
- pts[i].Y = r.ElevationM(i)
- }
-
- 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")
-}
-
-func PlotScore(r *Ride, startKm, endKm float64, outputFile string) {
- r.check()
-
- startIndex := 0
- for i := 0; i < r.Len(); i++ {
- if r.DistanceM(i) >= startKm*1000 {
- startIndex = i
- break
- }
- }
-
- pts1 := make(plotter.XYs, 0)
- endIndex := 0
- for i := startIndex + 1; i < r.Len(); i++ {
- if r.DistanceM(i) > endKm*1000 {
- endIndex = i
- break
- }
- score1 := Score(*r, startIndex, i)
- pts1 = append(pts1, plotter.XY{
- X: r.DistanceM(i) / 1000, // Convert distance to kilometers
- Y: score1,
- })
- }
- pts2 := make(plotter.XYs, 0)
- for i := startIndex; i < endIndex; i++ {
- score2 := Score(*r, i, endIndex)
- pts2 = append(pts2, plotter.XY{
- X: r.DistanceM(i) / 1000, // Convert distance to kilometers
- Y: score2,
- })
- }
-
- p := plot.New()
- p.Title.Text = "Ride Score Profile"
- p.X.Label.Text = "Distance (km)"
- p.Y.Label.Text = "Score"
-
- line1, err := plotter.NewLine(pts1)
- kcore.Expect(err, "failed to create line plot for score1")
- line1.Color = plotutil.Color(0) // First line color
- maxIndex := 0
- maxValue := pts1[0].Y
- for i, pt := range pts1 {
- if pt.Y > maxValue {
- maxValue = pt.Y
- maxIndex = i
- }
- }
- println("Max value of line 1 is at distance:", pts1[maxIndex].X, "km")
-
- line2, err := plotter.NewLine(pts2)
- kcore.Expect(err, "failed to create line plot for score2")
- line2.Color = plotutil.Color(1) // Second line color
-
- p.Add(line1, line2)
- p.Legend.Add("Score (start to i)", line1)
- p.Legend.Add("Score (i to end)", line2)
-
- err = p.Save(10*vg.Inch, 4*vg.Inch, outputFile)
- kcore.Expect(err, "failed to save plot")
-}