summaryrefslogtreecommitdiff
path: root/chart.go
blob: 063b495dbd53677ae7c83a872cd6e5dca9983618 (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
package main

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 renderChart(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, len(r.Points()))
	for i, point := range r.Points() {
		elevation[i].X = point.DistanceM / 1000
		elevation[i].Y = point.ElevationM
		if point.ElevationM > maxElev {
			maxElev = point.ElevationM
		}
	}

	for _, climb := range climbs {
		x0 := climb.Start().DistanceM / 1000
		x1 := climb.End().DistanceM / 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.Top().ElevationM * 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
}