Skip to content

Instantly share code, notes, and snippets.

@ajstarks
Last active November 18, 2015 16:41
Show Gist options
  • Save ajstarks/75cd99c95dc817116341 to your computer and use it in GitHub Desktop.
Save ajstarks/75cd99c95dc817116341 to your computer and use it in GitHub Desktop.
Sparkline experiment
package main
import (
"github.com/ajstarks/svgo"
"os"
)
var canvas = svg.New(os.Stdout)
// sparkline defines the components of the sparkline:
// text: label, data: data points, min/max: data range, rmin/rmax: highlighted range
type sparkline struct {
text string
data []float64
min, max, rmin, rmax float64
}
// vmap maps one range to another
func vmap(value, low1, high1, low2, high2 float64) float64 {
return low2 + (high2-low2)*(value-low1)/(high1-low1)
}
// datarange draws a datarange
func (s *sparkline) datarange(x, y, w, h int, color string) {
if s.rmin >= s.rmax || s.rmin < s.min || s.rmax > s.max {
return
}
_, dy1 := s.sparkdim(x, y, w, h, 0, s.rmax)
_, dy2 := s.sparkdim(x, y, w, h, 0, s.rmin)
canvas.Rect(x, y-dy1, w, dy1-dy2, "fill-opacity:0.2;fill:"+color)
}
// sparkdim returns the device coordinates
func (s *sparkline) sparkdim(x, y, w, h, i int, v float64) (int, int) {
dx := int(vmap(float64(i), 0, float64(len(s.data)), float64(x), float64(x+w)))
dy := int(vmap(v, s.min, s.max, 0, float64(h)))
return dx, dy
}
// label makes the sparkline label
func (s *sparkline) label(x, y, w, h int) {
canvas.Text(x-5, y, s.text, "text-anchor:end")
}
// bar makes a barchart
func (s *sparkline) bar(x, y, w, h int, color string) {
s.label(x, y, w, h)
canvas.Gstyle("stroke:" + color)
for i, v := range s.data {
dx, dy := s.sparkdim(x, y, w, h, i, v)
canvas.Line(dx, y, dx, y-dy)
}
canvas.Gend()
s.datarange(x, y, w, h, color)
}
// line makes a line graph
func (s *sparkline) line(x, y, w, h int, color string) {
s.label(x, y, w, h)
canvas.Gstyle("stroke:" + color)
for i := 0; i < len(s.data)-1; i++ {
dx1, dy1 := s.sparkdim(x, y, w, h, i, s.data[i])
dx2, dy2 := s.sparkdim(x, y, w, h, i+1, s.data[i+1])
canvas.Line(dx1, y-dy1, dx2, y-dy2)
}
canvas.Gend()
s.datarange(x, y, w, h, color)
}
// area makes a area chart sparkline
func (s *sparkline) area(x, y, w, h int, color string) {
s.label(x, y, w, h)
xa := make([]int, len(s.data)+1)
ya := make([]int, len(s.data)+1)
var dx, dy int
for i, v := range s.data {
dx, dy = s.sparkdim(x, y, w, h, i, v)
if i == 0 {
xa[0] = dx
ya[0] = y
}
xa[i+1] = dx
ya[i+1] = y - dy
}
xa = append(xa, dx)
ya = append(ya, y)
canvas.Polygon(xa, ya, "fill-opacity:0.7;fill:"+color)
s.datarange(x, y, w, h, color)
}
func main() {
sparks := []sparkline{
{text: "SNY", data: []float64{47.47, 46.64, 46.70, 47.49, 47.99, 48.24, 47.83, 49.41, 49.32, 50.86, 50.36, 49.76, 49.72, 50.23, 49.77, 49.69, 50.01, 48.45, 49.57, 49.23, 48.11}, min: 45, max: 55, rmin: 48, rmax: 50},
{text: "GOOG", data: []float64{608.42, 594.97, 594.89, 611.97, 625.80, 622.36, 622.69, 635.44, 629.25, 642.90, 635.98, 635.14, 623.24, 625.77, 621.35, 612.72, 614.66, 600.70, 606.25, 614.34, 597.79}, min: 570, max: 650, rmin: 600, rmax: 620},
{text: "AAPL", data: []float64{109.83, 108.60, 111.96, 114.22, 114.51, 113.83, 112.92, 114.72, 112.97, 113.43, 115.91, 115.78, 114.82, 113.72, 112.09, 109.68, 111.83, 108.80, 109.90, 111.86, 107.26}, min: 100, max: 120, rmin: 110, rmax: 115},
{text: "FAKE", data: []float64{10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150}, min: 0, max: 200, rmin: 20, rmax: 50},
}
width, height := 700, 700
x, y := 100, 200
linespacing := 75
canvas.Start(width, height)
canvas.Gstyle("font-family:sans-serif,Calibri;font-size:14pt")
for _, s := range sparks {
s.bar(x, y, 100, 50, "rgb(127,0,0)")
s.area(x+200, y, 100, 50, "lightsteelblue")
s.line(x+400, y, 100, 50, "black")
y += linespacing
}
canvas.Gend()
canvas.End()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment