...

Package plotter

import "gonum.org/v1/plot/plotter"
Overview
Index
Examples

Overview ▾

Package plotter defines a variety of standard Plotters for the plot package.

Plotters use the primitives provided by the plot package to draw to the data area of a plot. This package provides some standard data styles such as lines, scatter plots, box plots, labels, and more.

New* functions return an error if the data contains Inf, NaN, or is empty. Some of the New* functions return other plotter-specific errors too.

Example

Draw the plot logo.

Code:

p := plot.New()

plotter.DefaultLineStyle.Width = vg.Points(1)
plotter.DefaultGlyphStyle.Radius = vg.Points(3)

p.Y.Tick.Marker = plot.ConstantTicks([]plot.Tick{
    {Value: 0, Label: "0"}, {Value: 0.25, Label: ""}, {Value: 0.5, Label: "0.5"}, {Value: 0.75, Label: ""}, {Value: 1, Label: "1"},
})
p.X.Tick.Marker = plot.ConstantTicks([]plot.Tick{
    {Value: 0, Label: "0"}, {Value: 0.25, Label: ""}, {Value: 0.5, Label: "0.5"}, {Value: 0.75, Label: ""}, {Value: 1, Label: "1"},
})

pts := plotter.XYs{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 0.5, Y: 1}, {X: 0.5, Y: 0.6}, {X: 0, Y: 0.6}}
line, err := plotter.NewLine(pts)
if err != nil {
    log.Panic(err)
}
scatter, err := plotter.NewScatter(pts)
if err != nil {
    log.Panic(err)
}
p.Add(line, scatter)

pts = plotter.XYs{{X: 1, Y: 0}, {X: 0.75, Y: 0}, {X: 0.75, Y: 0.75}}
line, err = plotter.NewLine(pts)
if err != nil {
    log.Panic(err)
}
scatter, err = plotter.NewScatter(pts)
if err != nil {
    log.Panic(err)
}
p.Add(line, scatter)

pts = plotter.XYs{{X: 0.5, Y: 0.5}, {X: 1, Y: 0.5}}
line, err = plotter.NewLine(pts)
if err != nil {
    log.Panic(err)
}
scatter, err = plotter.NewScatter(pts)
if err != nil {
    log.Panic(err)
}
p.Add(line, scatter)

err = p.Save(100, 100, "testdata/plotLogo.png")
if err != nil {
    log.Panic(err)
}

Example (InvertedScale)

Code:

// This example is nearly identical to the LogScale, other than
// both the X and Y axes are inverted. InvertedScale expects to act
// on another Normalizer - which should allow for more flexibility
p := plot.New()
p.Title.Text = "Example of inverted axes"
p.Y.Scale = plot.InvertedScale{Normalizer: plot.LogScale{}}
p.X.Scale = plot.InvertedScale{Normalizer: plot.LinearScale{}}
p.Y.Tick.Marker = plot.LogTicks{Prec: -1}
p.X.Label.Text = "x"
p.Y.Label.Text = "f(x)"

f := plotter.NewFunction(math.Exp)
f.XMin = 0.2
f.XMax = 10

f.Color = color.RGBA{R: 255, A: 255}

p.Add(f, plotter.NewGrid())
p.Legend.Add("exp(x)", f)

// Neither .Min nor .Max for the X and Y axes are 'swapped'.
// The minimal value is retained in .Min, and the maximal
// value stays in .Max.
p.X.Min = f.XMin
p.X.Max = f.XMax
p.Y.Min = math.Exp(f.XMin)
p.Y.Max = math.Exp(f.XMax)

err := p.Save(10*vg.Centimeter, 10*vg.Centimeter, "testdata/invertedlogscale.png")
if err != nil {
    log.Panic(err)
}

Example (LogScale)

Example_logScale shows how to create a plot with a log-scale on the Y-axis.

Code:

p := plot.New()
p.Title.Text = "My Plot"
p.Y.Scale = plot.LogScale{}
p.Y.Tick.Marker = plot.LogTicks{Prec: -1}
p.X.Label.Text = "x"
p.Y.Label.Text = "f(x)"

f := plotter.NewFunction(math.Exp)
f.XMin = 0.2
f.XMax = 10
f.Color = color.RGBA{R: 255, A: 255}

p.Add(f, plotter.NewGrid())
p.Legend.Add("exp(x)", f)

p.X.Min = f.XMin
p.X.Max = f.XMax
p.Y.Min = math.Exp(f.XMin)
p.Y.Max = math.Exp(f.XMax)

err := p.Save(10*vg.Centimeter, 10*vg.Centimeter, "testdata/logscale.png")
if err != nil {
    log.Panic(err)
}

Example (LogScaleWithAutoRescale)

Example_logScaleWithAutoRescale shows how to create a plot with a log-scale on the Y-axis. The Y-axis is instructed to automatically adapt its range according to the underlying Y-ticker.

Code:

p := plot.New()
p.Title.Text = "My Plot"
p.Y.Scale = plot.LogScale{}
p.Y.Tick.Marker = plot.LogTicks{Prec: -1}
p.Y.AutoRescale = true
p.X.Label.Text = "x"
p.Y.Label.Text = "f(x)"

f := plotter.NewFunction(math.Exp)
f.XMin = 0.2
f.XMax = 10
f.Color = color.RGBA{R: 255, A: 255}

p.Add(f, plotter.NewGrid())
p.Legend.Add("exp(x)", f)

p.X.Min = f.XMin
p.X.Max = f.XMax
p.Y.Min = math.Exp(f.XMin)
p.Y.Max = math.Exp(f.XMax)

err := p.Save(10*vg.Centimeter, 10*vg.Centimeter, "testdata/logscale_autorescale.png")
if err != nil {
    log.Panic(err)
}

Example (Rotation)

Example_rotation gives some examples of rotating text.

Code:

n := 100
xmax := 2 * math.Pi

// Sin creates a sine curve.
sin := func(n int, xmax float64) plotter.XYs {
    xy := make(plotter.XYs, n)
    for i := 0; i < n; i++ {
        xy[i].X = xmax / float64(n) * float64(i)
        xy[i].Y = math.Sin(xy[i].X) * 100
    }
    return xy
}

// These points will make up our sine curve.
linePoints := sin(n, xmax)

// These points are our label locations.
labelPoints := sin(8, xmax)

p := plot.New()
p.Title.Text = "Rotation Example"
p.X.Label.Text = "X"
p.Y.Label.Text = "100 × Sine X"

l, err := plotter.NewLine(linePoints)
if err != nil {
    log.Panic(err)
}
l.LineStyle.Width = vg.Points(1)
l.LineStyle.Color = color.RGBA{B: 255, A: 255}

labelData := plotter.XYLabels{
    XYs:    labelPoints,
    Labels: []string{"0", "pi/4", "pi/2", "3pi/4", "pi", "5pi/4", "3pi/2", "7pi/4", "2pi"},
}

labels, err := plotter.NewLabels(labelData)
if err != nil {
    log.Panic(err)
}

for i := range labels.TextStyle {
    x := labels.XYs[i].X

    // Set the label rotation to the slope of the line, so the label is
    // parallel with the line.
    labels.TextStyle[i].Rotation = math.Atan(math.Cos(x))
    labels.TextStyle[i].XAlign = draw.XCenter
    labels.TextStyle[i].YAlign = draw.YCenter
    // Move the labels away from the line so they're more easily readable.
    if x >= math.Pi {
        labels.TextStyle[i].YAlign = draw.YTop
    } else {
        labels.TextStyle[i].YAlign = draw.YBottom
    }
}

p.Add(l, labels)

// Add boundary boxes for debugging.
p.Add(plotter.NewGlyphBoxes())

p.NominalX("0", "The number 1", "Number 2", "The number 3", "Number 4",
    "The number 5", "Number 6")

// Change the rotation of the X tick labels to make them fit better.
p.X.Tick.Label.Rotation = math.Pi / 5
p.X.Tick.Label.YAlign = draw.YCenter
p.X.Tick.Label.XAlign = draw.XRight

// Also change the rotation of the Y tick labels.
p.Y.Tick.Label.Rotation = math.Pi / 2
p.Y.Tick.Label.XAlign = draw.XCenter
p.Y.Tick.Label.YAlign = draw.YBottom

err = p.Save(200, 150, "testdata/rotation.png")
if err != nil {
    log.Panic(err)
}

Example (TimeSeries)

Example_timeSeries draws a time series.

Code:

rnd := rand.New(rand.NewSource(1))

// xticks defines how we convert and display time.Time values.
xticks := plot.TimeTicks{Format: "2006-01-02\n15:04"}

// randomPoints returns some random x, y points
// with some interesting kind of trend.
randomPoints := func(n int) plotter.XYs {
    const (
        month = 1
        day   = 1
        hour  = 1
        min   = 1
        sec   = 1
        nsec  = 1
    )
    pts := make(plotter.XYs, n)
    for i := range pts {
        date := time.Date(2007+i, month, day, hour, min, sec, nsec, time.UTC).Unix()
        pts[i].X = float64(date)
        pts[i].Y = float64(pts[i].X+10*rnd.Float64()) * 1e-9
    }
    return pts
}

n := 10
data := randomPoints(n)

p := plot.New()
p.Title.Text = "Time Series"
p.X.Tick.Marker = xticks
p.Y.Label.Text = "Number of Gophers\n(Billions)"
p.Add(plotter.NewGrid())

line, points, err := plotter.NewLinePoints(data)
if err != nil {
    log.Panic(err)
}
line.Color = color.RGBA{G: 255, A: 255}
points.Shape = draw.CircleGlyph{}
points.Color = color.RGBA{R: 255, A: 255}

p.Add(line, points)

err = p.Save(10*vg.Centimeter, 5*vg.Centimeter, "testdata/timeseries.png")
if err != nil {
    log.Panic(err)
}

Example (Volcano)

Code:

package plotter_test

import (
    "image/color"

    "gonum.org/v1/gonum/mat"
    "gonum.org/v1/plot"
    "gonum.org/v1/plot/palette"
    "gonum.org/v1/plot/plotter"
    "gonum.org/v1/plot/vg"
    "gonum.org/v1/plot/vg/draw"
)

type deciGrid struct{ mat.Matrix }

func (g deciGrid) Dims() (c, r int)   { r, c = g.Matrix.Dims(); return c, r }
func (g deciGrid) Z(c, r int) float64 { return g.Matrix.At(r, c) }
func (g deciGrid) X(c int) float64 {
    _, n := g.Matrix.Dims()
    if c < 0 || c >= n {
        panic("index out of range")
    }
    return 10 * float64(c)
}
func (g deciGrid) Y(r int) float64 {
    m, _ := g.Matrix.Dims()
    if r < 0 || r >= m {
        panic("index out of range")
    }
    return 10 * float64(r)
}

func Example_volcano() {
    var levels []float64
    for l := 100.5; l < mat.Max(volcano.Matrix.(*mat.Dense)); l += 5 {
        levels = append(levels, l)
    }
    c := plotter.NewContour(volcano, levels, palette.Rainbow(len(levels), (palette.Yellow+palette.Red)/2, palette.Blue, 1, 1, 1))
    quarterStyle := draw.LineStyle{
        Color:  color.Black,
        Width:  vg.Points(0.5),
        Dashes: []vg.Length{0.2, 0.4},
    }
    halfStyle := draw.LineStyle{
        Color:  color.Black,
        Width:  vg.Points(0.5),
        Dashes: []vg.Length{5, 2, 1, 2},
    }
    c.LineStyles = append(c.LineStyles, quarterStyle, halfStyle, quarterStyle)

    h := plotter.NewHeatMap(volcano, palette.Heat(len(levels)*2, 1))

    p := plot.New()
    p.Title.Text = "Maunga Whau Volcano"

    p.Add(h)
    p.Add(c)

    p.X.Padding = 0
    p.Y.Padding = 0
    _, p.X.Max, _, p.Y.Max = h.DataRange()

    if err := p.Save(10*vg.Centimeter, 10*vg.Centimeter, "testdata/volcano.png"); err != nil {
        panic(err)
    }
}

Index ▾

Variables
func CheckFloats(fs ...float64) error
func NewLinePoints(xys XYer) (*Line, *Scatter, error)
func PaletteThumbnailers(p palette.Palette) []plot.Thumbnailer
func Range(vs Valuer) (min, max float64)
func XYRange(xys XYer) (xmin, xmax, ymin, ymax float64)
type BarChart
    func NewBarChart(vs Valuer, width vg.Length) (*BarChart, error)
    func (b *BarChart) BarHeight(i int) float64
    func (b *BarChart) DataRange() (xmin, xmax, ymin, ymax float64)
    func (b *BarChart) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
    func (b *BarChart) Plot(c draw.Canvas, plt *plot.Plot)
    func (b *BarChart) StackOn(on *BarChart)
    func (b *BarChart) Thumbnail(c *draw.Canvas)
type BoxPlot
    func NewBoxPlot(w vg.Length, loc float64, values Valuer) (*BoxPlot, error)
    func (b *BoxPlot) DataRange() (float64, float64, float64, float64)
    func (b *BoxPlot) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
    func (b *BoxPlot) OutsideLabels(labels Labeller) (*Labels, error)
    func (b *BoxPlot) Plot(c draw.Canvas, plt *plot.Plot)
type ColorBar
    func (l *ColorBar) DataRange() (xmin, xmax, ymin, ymax float64)
    func (l *ColorBar) Plot(c draw.Canvas, p *plot.Plot)
type Contour
    func NewContour(g GridXYZ, levels []float64, p palette.Palette) *Contour
    func (h *Contour) DataRange() (xmin, xmax, ymin, ymax float64)
    func (h *Contour) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
    func (h *Contour) Plot(c draw.Canvas, plt *plot.Plot)
type Errors
type Field
    func NewField(f FieldXY) *Field
    func (f *Field) DataRange() (xmin, xmax, ymin, ymax float64)
    func (f *Field) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
    func (f *Field) Plot(c draw.Canvas, plt *plot.Plot)
type FieldXY
type Flow
type Function
    func NewFunction(f func(float64) float64) *Function
    func (f *Function) Plot(c draw.Canvas, p *plot.Plot)
    func (f Function) Thumbnail(c *draw.Canvas)
type GlyphBoxes
    func NewGlyphBoxes() *GlyphBoxes
    func (g GlyphBoxes) Plot(c draw.Canvas, plt *plot.Plot)
type Grid
    func NewGrid() *Grid
    func (g *Grid) Plot(c draw.Canvas, plt *plot.Plot)
type GridXYZ
type HeatMap
    func NewHeatMap(g GridXYZ, p palette.Palette) *HeatMap
    func (h *HeatMap) DataRange() (xmin, xmax, ymin, ymax float64)
    func (h *HeatMap) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
    func (h *HeatMap) Plot(c draw.Canvas, plt *plot.Plot)
type Histogram
    func NewHist(vs Valuer, n int) (*Histogram, error)
    func NewHistogram(xy XYer, n int) (*Histogram, error)
    func (h *Histogram) DataRange() (xmin, xmax, ymin, ymax float64)
    func (h *Histogram) Normalize(sum float64)
    func (h *Histogram) Plot(c draw.Canvas, p *plot.Plot)
    func (h *Histogram) Thumbnail(c *draw.Canvas)
type HistogramBin
type Image
    func NewImage(img image.Image, xmin, ymin, xmax, ymax float64) *Image
    func (img *Image) DataRange() (xmin, xmax, ymin, ymax float64)
    func (img *Image) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
    func (img *Image) Plot(c draw.Canvas, p *plot.Plot)
type Labeller
type Labels
    func NewLabels(d XYLabeller) (*Labels, error)
    func (l *Labels) DataRange() (xmin, xmax, ymin, ymax float64)
    func (l *Labels) GlyphBoxes(p *plot.Plot) []plot.GlyphBox
    func (l *Labels) Plot(c draw.Canvas, p *plot.Plot)
type Line
    func NewLine(xys XYer) (*Line, error)
    func (pts *Line) DataRange() (xmin, xmax, ymin, ymax float64)
    func (pts *Line) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
    func (pts *Line) Plot(c draw.Canvas, plt *plot.Plot)
    func (pts *Line) Thumbnail(c *draw.Canvas)
type Polygon
    func NewPolygon(xys ...XYer) (*Polygon, error)
    func (pts *Polygon) DataRange() (xmin, xmax, ymin, ymax float64)
    func (pts *Polygon) Plot(c draw.Canvas, plt *plot.Plot)
    func (pts *Polygon) Thumbnail(c *draw.Canvas)
type QuartPlot
    func NewQuartPlot(loc float64, values Valuer) (*QuartPlot, error)
    func (b *QuartPlot) DataRange() (float64, float64, float64, float64)
    func (b *QuartPlot) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
    func (b *QuartPlot) OutsideLabels(labels Labeller) (*Labels, error)
    func (b *QuartPlot) Plot(c draw.Canvas, plt *plot.Plot)
type Sankey
    func NewSankey(flows ...Flow) (*Sankey, error)
    func (s *Sankey) DataRange() (xmin, xmax, ymin, ymax float64)
    func (s *Sankey) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
    func (s *Sankey) Plot(c draw.Canvas, plt *plot.Plot)
    func (s *Sankey) StockRange(label string, category int) (min, max float64, err error)
    func (s *Sankey) Thumbnailers() (legendLabels []string, thumbnailers []plot.Thumbnailer)
type Scatter
    func NewScatter(xys XYer) (*Scatter, error)
    func (pts *Scatter) DataRange() (xmin, xmax, ymin, ymax float64)
    func (pts *Scatter) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
    func (pts *Scatter) Plot(c draw.Canvas, plt *plot.Plot)
    func (pts *Scatter) Thumbnail(c *draw.Canvas)
type StepKind
type ValueLabels
    func (vs ValueLabels) Label(i int) string
    func (vs ValueLabels) Len() int
    func (vs ValueLabels) Value(i int) float64
type Valuer
type Values
    func CopyValues(vs Valuer) (Values, error)
    func (vs Values) Len() int
    func (vs Values) Value(i int) float64
type XErrorBars
    func NewXErrorBars(xerrs interface {XYer XErrorer}) (*XErrorBars, error)
    func (e *XErrorBars) DataRange() (xmin, xmax, ymin, ymax float64)
    func (e *XErrorBars) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
    func (e *XErrorBars) Plot(c draw.Canvas, p *plot.Plot)
type XErrorer
type XErrors
    func (xe XErrors) XError(i int) (float64, float64)
type XValues
    func (xs XValues) Value(i int) float64
type XY
type XYLabeller
type XYLabels
    func (l XYLabels) Label(i int) string
type XYValues
    func (xy XYValues) XY(i int) (float64, float64)
type XYZ
type XYZer
type XYZs
    func CopyXYZs(data XYZer) (XYZs, error)
    func (xyz XYZs) Len() int
    func (xyz XYZs) XY(i int) (float64, float64)
    func (xyz XYZs) XYZ(i int) (float64, float64, float64)
type XYer
type XYs
    func CopyXYs(data XYer) (XYs, error)
    func (xys XYs) Len() int
    func (xys XYs) XY(i int) (float64, float64)
type YErrorBars
    func NewYErrorBars(yerrs interface {XYer YErrorer}) (*YErrorBars, error)
    func (e *YErrorBars) DataRange() (xmin, xmax, ymin, ymax float64)
    func (e *YErrorBars) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
    func (e *YErrorBars) Plot(c draw.Canvas, p *plot.Plot)
type YErrorer
type YErrors
    func (ye YErrors) YError(i int) (float64, float64)
type YValues
    func (ys YValues) Value(i int) float64

Package files

barchart.go boxplot.go colorbar.go conrec.go contour.go errbars.go field.go functions.go glyphbox.go grid.go heat.go histogram.go image.go johnson.go labels.go line.go palettethumbnailer.go plotter.go polygon.go quartile.go sankey.go scatter.go

Variables

var (
    // DefaultFont is the default font for label text.
    DefaultFont = plot.DefaultFont

    // DefaultFontSize is the default font.
    DefaultFontSize = vg.Points(10)
)
var (
    // DefaultLineStyle is the default style for drawing
    // lines.
    DefaultLineStyle = draw.LineStyle{
        Color:    color.Black,
        Width:    vg.Points(1),
        Dashes:   []vg.Length{},
        DashOffs: 0,
    }

    // DefaultGlyphStyle is the default style used
    // for gyph marks.
    DefaultGlyphStyle = draw.GlyphStyle{
        Color:  color.Black,
        Radius: vg.Points(2.5),
        Shape:  draw.RingGlyph{},
    }
)
var (
    ErrInfinity = errors.New("plotter: infinite data point")
    ErrNaN      = errors.New("plotter: NaN data point")
    ErrNoData   = errors.New("plotter: no data points")
)
var (
    // DefaultQuartMedianStyle is a fat dot.
    DefaultQuartMedianStyle = draw.GlyphStyle{
        Color:  color.Black,
        Radius: vg.Points(1.5),
        Shape:  draw.CircleGlyph{},
    }

    // DefaultQuartWhiskerStyle is a hairline.
    DefaultQuartWhiskerStyle = draw.LineStyle{
        Color:    color.Black,
        Width:    vg.Points(0.5),
        Dashes:   []vg.Length{},
        DashOffs: 0,
    }
)

DefaultCapWidth is the default width of error bar caps.

var DefaultCapWidth = vg.Points(5)
var (
    // DefaultGridLineStyle is the default style for grid lines.
    DefaultGridLineStyle = draw.LineStyle{
        Color: color.Gray{128},
        Width: vg.Points(0.25),
    }
)

func CheckFloats

func CheckFloats(fs ...float64) error

CheckFloats returns an error if any of the arguments are NaN or Infinity.

func NewLinePoints

func NewLinePoints(xys XYer) (*Line, *Scatter, error)

NewLinePoints returns both a Line and a Points for the given point data.

func PaletteThumbnailers

func PaletteThumbnailers(p palette.Palette) []plot.Thumbnailer

PaletteThumbnailers creates a slice of plot.Thumbnailers that can be used to add legend entries for the colors in a color palette.

func Range

func Range(vs Valuer) (min, max float64)

Range returns the minimum and maximum values.

func XYRange

func XYRange(xys XYer) (xmin, xmax, ymin, ymax float64)

XYRange returns the minimum and maximum x and y values.

type BarChart

A BarChart presents grouped data with rectangular bars with lengths proportional to the data values.

type BarChart struct {
    Values

    // Width is the width of the bars.
    Width vg.Length

    // Color is the fill color of the bars.
    Color color.Color

    // LineStyle is the style of the outline of the bars.
    draw.LineStyle

    // Offset is added to the X location of each bar.
    // When the Offset is zero, the bars are drawn
    // centered at their X location.
    Offset vg.Length

    // XMin is the X location of the first bar.  XMin
    // can be changed to move groups of bars
    // down the X axis in order to make grouped
    // bar charts.
    XMin float64

    // Horizontal dictates whether the bars should be in the vertical
    // (default) or horizontal direction. If Horizontal is true, all
    // X locations and distances referred to here will actually be Y
    // locations and distances.
    Horizontal bool
    // contains filtered or unexported fields
}

Example

Code:

// Create the plot values and labels.
values := plotter.Values{0.5, 10, 20, 30}
verticalLabels := []string{"A", "B", "C", "D"}
horizontalLabels := []string{"Label A", "Label B", "Label C", "Label D"}

// Create a vertical BarChart
p1 := plot.New()
verticalBarChart, err := plotter.NewBarChart(values, 0.5*vg.Centimeter)
if err != nil {
    log.Panic(err)
}
p1.Add(verticalBarChart)
p1.NominalX(verticalLabels...)
err = p1.Save(100, 100, "testdata/verticalBarChart.png")
if err != nil {
    log.Panic(err)
}

// Create a horizontal BarChart
p2 := plot.New()
horizontalBarChart, err := plotter.NewBarChart(values, 0.5*vg.Centimeter)
horizontalBarChart.Horizontal = true // Specify a horizontal BarChart.
if err != nil {
    log.Panic(err)
}
p2.Add(horizontalBarChart)
p2.NominalY(horizontalLabels...)
err = p2.Save(100, 100, "testdata/horizontalBarChart.png")
if err != nil {
    log.Panic(err)
}

// Now, make a different type of BarChart.
groupA := plotter.Values{20, 35, 30, 35, 27}
groupB := plotter.Values{25, 32, 34, 20, 25}
groupC := plotter.Values{12, 28, 15, 21, 8}
groupD := plotter.Values{30, 42, 6, 9, 12}

p := plot.New()
p.Title.Text = "Bar chart"
p.Y.Label.Text = "Heights"

w := vg.Points(8)

barsA, err := plotter.NewBarChart(groupA, w)
if err != nil {
    log.Panic(err)
}
barsA.Color = color.RGBA{R: 255, A: 255}
barsA.Offset = -w / 2

barsB, err := plotter.NewBarChart(groupB, w)
if err != nil {
    log.Panic(err)
}
barsB.Color = color.RGBA{R: 196, G: 196, A: 255}
barsB.Offset = w / 2

barsC, err := plotter.NewBarChart(groupC, w)
if err != nil {
    log.Panic(err)
}
barsC.XMin = 6
barsC.Color = color.RGBA{B: 255, A: 255}
barsC.Offset = -w / 2

barsD, err := plotter.NewBarChart(groupD, w)
if err != nil {
    log.Panic(err)
}
barsD.Color = color.RGBA{B: 255, R: 255, A: 255}
barsD.XMin = 6
barsD.Offset = w / 2

p.Add(barsA, barsB, barsC, barsD)
p.Legend.Add("A", barsA)
p.Legend.Add("B", barsB)
p.Legend.Add("C", barsC)
p.Legend.Add("D", barsD)
p.Legend.Top = true
p.NominalX("Zero", "One", "Two", "Three", "Four", "",
    "Six", "Seven", "Eight", "Nine", "Ten")

p.Add(plotter.NewGlyphBoxes())
err = p.Save(300, 250, "testdata/barChart2.png")
if err != nil {
    log.Panic(err)
}

// Now, make a stacked BarChart.
p = plot.New()
p.Title.Text = "Bar chart"
p.Y.Label.Text = "Heights"

w = vg.Points(15)

barsA, err = plotter.NewBarChart(groupA, w)
if err != nil {
    log.Panic(err)
}
barsA.Color = color.RGBA{R: 255, A: 255}
barsA.Offset = -w / 2

barsB, err = plotter.NewBarChart(groupB, w)
if err != nil {
    log.Panic(err)
}
barsB.Color = color.RGBA{R: 196, G: 196, A: 255}
barsB.StackOn(barsA)

barsC, err = plotter.NewBarChart(groupC, w)
if err != nil {
    log.Panic(err)
}
barsC.Offset = w / 2
barsC.Color = color.RGBA{B: 255, A: 255}

barsD, err = plotter.NewBarChart(groupD, w)
if err != nil {
    log.Panic(err)
}
barsD.StackOn(barsC)
barsD.Color = color.RGBA{B: 255, R: 255, A: 255}

p.Add(barsA, barsB, barsC, barsD)
p.Legend.Add("A", barsA)
p.Legend.Add("B", barsB)
p.Legend.Add("C", barsC)
p.Legend.Add("D", barsD)
p.Legend.Top = true
p.NominalX("Zero", "One", "Two", "Three", "Four")

p.Add(plotter.NewGlyphBoxes())
err = p.Save(250, 250, "testdata/stackedBarChart.png")
if err != nil {
    log.Panic(err)
}

Example (PositiveNegative)

This example shows a bar chart with both positive and negative values.

Code:

rnd := rand.New(rand.NewSource(1))

// Create random data points between -1 and 1.
const n = 6
data1 := make(plotter.Values, n)
data2 := make(plotter.Values, n)
net := make(plotter.XYs, n) // net = data1 + data2
for i := 0; i < n; i++ {
    data1[i] = rnd.Float64()*2 - 1
    data2[i] = rnd.Float64()*2 - 1
    net[i].X = data1[i] + data2[i]
    net[i].Y = float64(i)
}

// splitBySign splits an array into two arrays containing the positive and
// negative values, respectively, from the original array.
splitBySign := func(d plotter.Values) (pos, neg plotter.Values) {
    pos = make(plotter.Values, len(d))
    neg = make(plotter.Values, len(d))
    for i, v := range d {
        if v > 0 {
            pos[i] = v
        } else {
            neg[i] = v
        }
    }
    return
}

data1Pos, data1Neg := splitBySign(data1)
data2Pos, data2Neg := splitBySign(data2)

const barWidth = 0.3 * vg.Centimeter
pos1, err := plotter.NewBarChart(data1Pos, barWidth)
if err != nil {
    log.Panic(err)
}
pos2, err := plotter.NewBarChart(data2Pos, barWidth)
if err != nil {
    log.Panic(err)
}
neg1, err := plotter.NewBarChart(data1Neg, barWidth)
if err != nil {
    log.Panic(err)
}
neg2, err := plotter.NewBarChart(data2Neg, barWidth)
if err != nil {
    log.Panic(err)
}

netDots, err := plotter.NewScatter(net)
if err != nil {
    log.Panic(err)
}
netDots.Radius = vg.Points(1.25)

pos2.StackOn(pos1) // Specify that pos2 goes on top of pos1.
neg2.StackOn(neg1) // Specify that neg2 goes on top of neg1.

color1 := color.NRGBA{R: 112, G: 22, B: 0, A: 255}
color2 := color.NRGBA{R: 91, G: 194, B: 54, A: 100}

pos1.Color, neg1.Color = color1, color1
pos2.Color, neg2.Color = color2, color2

// Specify that we want a horizontal bar chart.
pos1.Horizontal, pos2.Horizontal, neg1.Horizontal, neg2.Horizontal = true, true, true, true

// Create a line at zero.
zero, err := plotter.NewLine(plotter.XYs{{0, 0}, {0, 5}})
if err != nil {
    log.Panic(err)
}

p := plot.New()
p.Add(zero, pos1, pos2, neg1, neg2, netDots)
p.NominalY("Alpha", "Bravo", "Charlie", "Echo", "Foxtrot", "Golf")

p.Legend.Add("1", pos1)
p.Legend.Add("2", pos2)
p.Legend.Add("Sum", netDots)
p.Legend.Top = true
p.Legend.ThumbnailWidth = 2 * vg.Millimeter
p.Legend.TextStyle.Font.Size = 2 * vg.Millimeter

err = p.Save(100, 100, "testdata/barChart_positiveNegative.png")
if err != nil {
    log.Panic(err)
}

func NewBarChart

func NewBarChart(vs Valuer, width vg.Length) (*BarChart, error)

NewBarChart returns a new bar chart with a single bar for each value. The bars heights correspond to the values and their x locations correspond to the index of their value in the Valuer.

func (*BarChart) BarHeight

func (b *BarChart) BarHeight(i int) float64

BarHeight returns the maximum y value of the ith bar, taking into account any bars upon which it is stacked.

func (*BarChart) DataRange

func (b *BarChart) DataRange() (xmin, xmax, ymin, ymax float64)

DataRange implements the plot.DataRanger interface.

func (*BarChart) GlyphBoxes

func (b *BarChart) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox

GlyphBoxes implements the GlyphBoxer interface.

func (*BarChart) Plot

func (b *BarChart) Plot(c draw.Canvas, plt *plot.Plot)

Plot implements the plot.Plotter interface.

func (*BarChart) StackOn

func (b *BarChart) StackOn(on *BarChart)

StackOn stacks a bar chart on top of another, and sets the XMin and Offset to that of the chart upon which it is being stacked.

func (*BarChart) Thumbnail

func (b *BarChart) Thumbnail(c *draw.Canvas)

Thumbnail fulfills the plot.Thumbnailer interface.

type BoxPlot

BoxPlot implements the Plotter interface, drawing a boxplot to represent the distribution of values.

type BoxPlot struct {

    // Offset is added to the x location of each box.
    // When the Offset is zero, the boxes are drawn
    // centered at their x location.
    Offset vg.Length

    // Width is the width used to draw the box.
    Width vg.Length

    // CapWidth is the width of the cap used to top
    // off a whisker.
    CapWidth vg.Length

    // GlyphStyle is the style of the outside point glyphs.
    GlyphStyle draw.GlyphStyle

    // FillColor is the color used to fill the box.
    // The default is no fill.
    FillColor color.Color

    // BoxStyle is the line style for the box.
    BoxStyle draw.LineStyle

    // MedianStyle is the line style for the median line.
    MedianStyle draw.LineStyle

    // WhiskerStyle is the line style used to draw the
    // whiskers.
    WhiskerStyle draw.LineStyle

    // Horizontal dictates whether the BoxPlot should be in the vertical
    // (default) or horizontal direction.
    Horizontal bool
    // contains filtered or unexported fields
}

Example

Code:

rnd := rand.New(rand.NewSource(1))

// Create the sample data.
const n = 100
uniform := make(plotter.ValueLabels, n)
normal := make(plotter.ValueLabels, n)
expon := make(plotter.ValueLabels, n)
for i := 0; i < n; i++ {
    uniform[i].Value = rnd.Float64()
    uniform[i].Label = fmt.Sprintf("%4.4f", uniform[i].Value)
    normal[i].Value = rnd.NormFloat64()
    normal[i].Label = fmt.Sprintf("%4.4f", normal[i].Value)
    expon[i].Value = rnd.ExpFloat64()
    expon[i].Label = fmt.Sprintf("%4.4f", expon[i].Value)
}

// Make boxes for our data and add them to the plot.
uniBox, err := plotter.NewBoxPlot(vg.Points(20), 0, uniform)
if err != nil {
    log.Panic(err)
}
uniBox.FillColor = color.RGBA{127, 188, 165, 1}
normBox, err := plotter.NewBoxPlot(vg.Points(20), 1, normal)
if err != nil {
    log.Panic(err)
}
normBox.FillColor = color.RGBA{127, 188, 165, 1}
expBox, err := plotter.NewBoxPlot(vg.Points(20), 2, expon)
if err != nil {
    log.Panic(err)
}
expBox.FillColor = color.RGBA{127, 188, 165, 1}

// Make a vertical box plot.
uniLabels, err := uniBox.OutsideLabels(uniform)
if err != nil {
    log.Panic(err)
}
normLabels, err := normBox.OutsideLabels(normal)
if err != nil {
    log.Panic(err)
}
expLabels, err := expBox.OutsideLabels(expon)
if err != nil {
    log.Panic(err)
}

p1 := plot.New()
p1.Title.Text = "Vertical Box Plot"
p1.Y.Label.Text = "plotter.Values"
p1.Y.Max = 6
p1.Add(uniBox, uniLabels, normBox, normLabels, expBox, expLabels)

// Set the X axis of the plot to nominal with
// the given names for x=0, x=1 and x=2.
p1.NominalX("Uniform\nDistribution", "Normal\nDistribution",
    "Exponential\nDistribution")

err = p1.Save(200, 200, "testdata/verticalBoxPlot.png")
if err != nil {
    log.Panic(err)
}

// Now, make the same plot but horizontal.
normBox.Horizontal = true
expBox.Horizontal = true
uniBox.Horizontal = true
// We can use the same plotters but the labels need to be recreated.
uniLabels, err = uniBox.OutsideLabels(uniform)
if err != nil {
    log.Panic(err)
}
normLabels, err = normBox.OutsideLabels(normal)
if err != nil {
    log.Panic(err)
}
expLabels, err = expBox.OutsideLabels(expon)
if err != nil {
    log.Panic(err)
}

p2 := plot.New()
p2.Title.Text = "Horizontal Box Plot"
p2.X.Label.Text = "plotter.Values"

p2.Add(uniBox, uniLabels, normBox, normLabels, expBox, expLabels)

// Set the Y axis of the plot to nominal with
// the given names for y=0, y=1 and y=2.
p2.NominalY("Uniform\nDistribution", "Normal\nDistribution",
    "Exponential\nDistribution")

err = p2.Save(200, 200, "testdata/horizontalBoxPlot.png")
if err != nil {
    log.Panic(err)
}

// Now, make a grouped box plot.
p3 := plot.New()
p3.Title.Text = "Box Plot"
p3.Y.Label.Text = "plotter.Values"

w := vg.Points(20)
for x := 0.0; x < 3.0; x++ {
    b0, err := plotter.NewBoxPlot(w, x, uniform)
    if err != nil {
        log.Panic(err)
    }
    b0.Offset = -w - vg.Points(3)
    b1, err := plotter.NewBoxPlot(w, x, normal)
    if err != nil {
        log.Panic(err)
    }
    b2, err := plotter.NewBoxPlot(w, x, expon)
    if err != nil {
        log.Panic(err)
    }
    b2.Offset = w + vg.Points(3)
    p3.Add(b0, b1, b2)
}
// Add a GlyphBox plotter for debugging.
p3.Add(plotter.NewGlyphBoxes())

// Set the X axis of the plot to nominal with
// the given names for x=0, x=1 and x=2.
p3.NominalX("Group 0", "Group 1", "Group 2")
err = p3.Save(300, 300, "testdata/groupedBoxPlot.png")
if err != nil {
    log.Panic(err)
}

func NewBoxPlot

func NewBoxPlot(w vg.Length, loc float64, values Valuer) (*BoxPlot, error)

NewBoxPlot returns a new BoxPlot that represents the distribution of the given values. The style of the box plot is that used for Tukey's schematic plots in “Exploratory Data Analysis.”

An error is returned if the boxplot is created with no values.

The fence values are 1.5x the interquartile before the first quartile and after the third quartile. Any value that is outside of the fences are drawn as Outside points. The adjacent values (to which the whiskers stretch) are the minimum and maximum values that are not outside the fences.

func (*BoxPlot) DataRange

func (b *BoxPlot) DataRange() (float64, float64, float64, float64)

DataRange returns the minimum and maximum x and y values, implementing the plot.DataRanger interface.

func (*BoxPlot) GlyphBoxes

func (b *BoxPlot) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox

GlyphBoxes returns a slice of GlyphBoxes for the points and for the median line of the boxplot, implementing the plot.GlyphBoxer interface

func (*BoxPlot) OutsideLabels

func (b *BoxPlot) OutsideLabels(labels Labeller) (*Labels, error)

OutsideLabels returns a *Labels that will plot a label for each of the outside points. The labels are assumed to correspond to the points used to create the box plot.

func (*BoxPlot) Plot

func (b *BoxPlot) Plot(c draw.Canvas, plt *plot.Plot)

Plot draws the BoxPlot on Canvas c and Plot plt.

type ColorBar

ColorBar is a plot.Plotter that draws a color bar legend for a ColorMap.

type ColorBar struct {
    ColorMap palette.ColorMap

    // Vertical determines wether the legend will be
    // plotted vertically or horizontally.
    // The default is false (horizontal).
    Vertical bool

    // Colors specifies the number of colors to be
    // shown in the legend. If Colors is not specified,
    // a default will be used.
    Colors int
}

Example (Horizontal)

Code:

p := plot.New()
p.HideY()
p.X.Padding = 0
p.Title.Text = "Title"

l := &plotter.ColorBar{ColorMap: moreland.ExtendedBlackBody()}
l.ColorMap.SetMin(0.5)
l.ColorMap.SetMax(1.5)
p.Add(l)

if err := p.Save(300, 48, "testdata/colorBarHorizontal.png"); err != nil {
    log.Panic(err)
}

Example (Vertical)

Code:

p := plot.New()
l := &plotter.ColorBar{ColorMap: moreland.ExtendedBlackBody()}
l.ColorMap.SetMin(0.5)
l.ColorMap.SetMax(1.5)
l.Vertical = true
p.Add(l)
p.HideX()
p.Y.Padding = 0
p.Title.Text = "Title"

if err := p.Save(40, 300, "testdata/colorBarVertical.png"); err != nil {
    log.Panic(err)
}

func (*ColorBar) DataRange

func (l *ColorBar) DataRange() (xmin, xmax, ymin, ymax float64)

DataRange implements the DataRange method of the plot.DataRanger interface.

func (*ColorBar) Plot

func (l *ColorBar) Plot(c draw.Canvas, p *plot.Plot)

Plot implements the Plot method of the plot.Plotter interface.

type Contour

Contour implements the Plotter interface, drawing a contour plot of the values in the GridXYZ field.

type Contour struct {
    GridXYZ GridXYZ

    // Levels describes the contour heights to plot.
    Levels []float64

    // LineStyles is the set of styles for contour
    // lines. Line styles are are applied to each level
    // in order, modulo the length of LineStyles.
    LineStyles []draw.LineStyle

    // Palette is the color palette used to render
    // the heat map. If Palette is nil or has no
    // defined color, the Contour LineStyle color
    // is used.
    Palette palette.Palette

    // Underflow and Overflow are colors used to draw
    // contours outside the dynamic range defined
    // by Min and Max.
    Underflow color.Color
    Overflow  color.Color

    // Min and Max define the dynamic range of the
    // heat map.
    Min, Max float64
}

Example

Code:

rnd := rand.New(rand.NewSource(1234))

const stddev = 2
data := make([]float64, 6400)
for i := range data {
    r := float64(i/80) - 40
    c := float64(i%80) - 40

    data[i] = rnd.NormFloat64()*stddev + math.Hypot(r, c)
}

var (
    grid   = unitGrid{mat.NewDense(80, 80, data)}
    levels = []float64{-1, 3, 7, 9, 13, 15, 19, 23, 27, 31}

    c = plotter.NewContour(
        grid,
        levels,
        palette.Rainbow(10, palette.Blue, palette.Red, 1, 1, 1),
    )
)

p := plot.New()
p.Title.Text = "Contour"
p.X.Padding = 0
p.Y.Padding = 0
p.X.Max = 79.5
p.Y.Max = 79.5

p.Add(c)

err := p.Save(10*vg.Centimeter, 10*vg.Centimeter, "testdata/contour.png")
if err != nil {
    log.Fatalf("could not save plot: %+v", err)
}

func NewContour

func NewContour(g GridXYZ, levels []float64, p palette.Palette) *Contour

NewContour creates as new contour plotter for the given data, using the provided palette. If levels is nil, contours are generated for the 0.01, 0.05, 0.25, 0.5, 0.75, 0.95 and 0.99 quantiles. If g has Min and Max methods that return a float, those returned values are used to set the respective Contour fields. If the returned Contour is used when Min is greater than Max, the Plot method will panic.

func (*Contour) DataRange

func (h *Contour) DataRange() (xmin, xmax, ymin, ymax float64)

DataRange implements the DataRange method of the plot.DataRanger interface.

func (*Contour) GlyphBoxes

func (h *Contour) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox

GlyphBoxes implements the GlyphBoxes method of the plot.GlyphBoxer interface.

func (*Contour) Plot

func (h *Contour) Plot(c draw.Canvas, plt *plot.Plot)

Plot implements the Plot method of the plot.Plotter interface.

type Errors

Errors is a slice of low and high error values.

type Errors []struct{ Low, High float64 }

Example

ExampleErrors draws points and error bars.

Code:

rnd := rand.New(rand.NewSource(1))

randomError := func(n int) plotter.Errors {
    err := make(plotter.Errors, n)
    for i := range err {
        err[i].Low = rnd.Float64()
        err[i].High = rnd.Float64()
    }
    return err
}
// randomPoints returns some random x, y points
// with some interesting kind of trend.
randomPoints := func(n int) plotter.XYs {
    pts := make(plotter.XYs, n)
    for i := range pts {
        if i == 0 {
            pts[i].X = rnd.Float64()
        } else {
            pts[i].X = pts[i-1].X + rnd.Float64()
        }
        pts[i].Y = pts[i].X + 10*rnd.Float64()
    }
    return pts
}

type errPoints struct {
    plotter.XYs
    plotter.YErrors
    plotter.XErrors
}

n := 15
data := errPoints{
    XYs:     randomPoints(n),
    YErrors: plotter.YErrors(randomError(n)),
    XErrors: plotter.XErrors(randomError(n)),
}

p := plot.New()
scatter, err := plotter.NewScatter(data)
if err != nil {
    log.Panic(err)
}
scatter.Shape = draw.CrossGlyph{}
xerrs, err := plotter.NewXErrorBars(data)
if err != nil {
    log.Panic(err)
}
yerrs, err := plotter.NewYErrorBars(data)
if err != nil {
    log.Panic(err)
}
p.Add(scatter, xerrs, yerrs)

err = p.Save(200, 200, "testdata/errorBars.png")
if err != nil {
    log.Panic(err)
}

type Field

Field implements the Plotter interface, drawing a vector field of the values in the FieldXY field.

type Field struct {
    FieldXY FieldXY

    // DrawGlyph is the user hook to draw a field
    // vector glyph. The function should draw a unit
    // vector to (1, 0) on the vg.Canvas, c with the
    // sty LineStyle. The Field plotter will rotate
    // and scale the unit vector appropriately.
    // If the magnitude of v is zero, no scaling or
    // rotation is performed.
    //
    // The direction and magnitude of v can be used
    // to determine properties of the glyph drawing
    // but should not be used to determine size or
    // directions of the glyph.
    //
    // If DrawGlyph is nil, a simple arrow will be
    // drawn.
    DrawGlyph func(c vg.Canvas, sty draw.LineStyle, v XY)

    // LineStyle is the style of the line used to
    // render vectors when DrawGlyph is nil.
    // Otherwise it is passed to DrawGlyph.
    LineStyle draw.LineStyle
    // contains filtered or unexported fields
}

Example

Code:

f := plotter.NewField(field{
    r: 17, c: 19,
    fn: func(x, y float64) plotter.XY {
        return plotter.XY{
            X: y,
            Y: -x,
        }
    },
})
f.LineStyle.Width = 0.2

p := plot.New()
p.Title.Text = "Vector field"
p.X.Tick.Marker = integerTicks{}
p.Y.Tick.Marker = integerTicks{}

p.Add(f)

img := vgimg.New(250, 175)
dc := draw.New(img)

p.Draw(dc)
w, err := os.Create("testdata/field.png")
if err != nil {
    log.Panic(err)
}
png := vgimg.PngCanvas{Canvas: img}
if _, err = png.WriteTo(w); err != nil {
    log.Panic(err)
}

Example (Colors)

Code:

f := plotter.NewField(field{
    r: 5, c: 9,
    fn: func(x, y float64) plotter.XY {
        return plotter.XY{
            X: -0.75*x + y,
            Y: -0.75*y - x,
        }
    },
})

pal := moreland.ExtendedBlackBody()
pal.SetMin(0)
pal.SetMax(1.1) // Use 1.1 to make highest magnitude vectors visible on white.

// Provide a DrawGlyph function to render a custom
// vector instead of the default monochrome arrow.
f.DrawGlyph = func(c vg.Canvas, sty draw.LineStyle, v plotter.XY) {
    c.Push()
    defer c.Pop()
    mag := math.Hypot(v.X, v.Y)
    var pa vg.Path
    if mag == 0 {
        // Draw a black dot for zero vectors.
        c.SetColor(color.Black)
        pa.Move(vg.Point{X: sty.Width})
        pa.Arc(vg.Point{}, sty.Width, 0, 2*math.Pi)
        pa.Close()
        c.Fill(pa)
        return
    }
    // Choose a color from the palette for the magnitude.
    col, err := pal.At(mag)
    if err != nil {
        panic(err)
    }
    c.SetColor(col)
    pa.Move(vg.Point{})
    pa.Line(vg.Point{X: 1, Y: 0})
    pa.Close()
    c.Stroke(pa)
}

p := plot.New()
p.Title.Text = "Vortex"
p.X.Tick.Marker = integerTicks{}
p.Y.Tick.Marker = integerTicks{}

p.Add(f)

img := vgimg.New(250, 175)
dc := draw.New(img)

p.Draw(dc)
w, err := os.Create("testdata/color_field.png")
if err != nil {
    log.Panic(err)
}
defer w.Close()
png := vgimg.PngCanvas{Canvas: img}
if _, err = png.WriteTo(w); err != nil {
    log.Panic(err)
}

Example (Gophers)

Code:

file, err := os.Open("testdata/gopher_running.png")
if err != nil {
    log.Panic(err)
}
defer file.Close()
gopher, err := png.Decode(file)
if err != nil {
    log.Panic(err)
}

f := plotter.NewField(field{
    r: 5, c: 9,
    fn: func(x, y float64) plotter.XY {
        return plotter.XY{
            X: -0.75*x + y,
            Y: -0.75*y - x,
        }
    },
})

// Provide a DrawGlyph function to render a custom
// vector glyph instead of the default arrow.
f.DrawGlyph = func(c vg.Canvas, _ draw.LineStyle, v plotter.XY) {
    // The canvas is unscaled if the vector has a zero
    // magnitude, so return in that case.
    if math.Hypot(v.X, v.Y) == 0 {
        return
    }
    // Vector glyphs are scaled to half unit length by the
    // plotter, so scale the gopher to twice unit size so
    // it fits the cell, and center on the cell.
    c.Translate(vg.Point{X: -1, Y: -1})
    c.DrawImage(vg.Rectangle{Max: vg.Point{X: 2, Y: 2}}, gopher)
}

p := plot.New()
p.Title.Text = "Gopher vortex"
p.X.Tick.Marker = integerTicks{}
p.Y.Tick.Marker = integerTicks{}

p.Add(f)

img := vgimg.New(250, 175)
dc := draw.New(img)

p.Draw(dc)
w, err := os.Create("testdata/gopher_field.png")
if err != nil {
    log.Panic(err)
}
png := vgimg.PngCanvas{Canvas: img}
if _, err = png.WriteTo(w); err != nil {
    log.Panic(err)
}

func NewField

func NewField(f FieldXY) *Field

NewField creates a new vector field plotter.

func (*Field) DataRange

func (f *Field) DataRange() (xmin, xmax, ymin, ymax float64)

DataRange implements the DataRange method of the plot.DataRanger interface.

func (*Field) GlyphBoxes

func (f *Field) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox

GlyphBoxes implements the GlyphBoxes method of the plot.GlyphBoxer interface.

func (*Field) Plot

func (f *Field) Plot(c draw.Canvas, plt *plot.Plot)

Plot implements the Plot method of the plot.Plotter interface.

type FieldXY

FieldXY describes a two dimensional vector field where the X and Y coordinates are arranged on a rectangular grid.

type FieldXY interface {
    // Dims returns the dimensions of the grid.
    Dims() (c, r int)

    // Vector returns the value of a vector field at (c, r).
    // It will panic if c or r are out of bounds for the field.
    Vector(c, r int) XY

    // X returns the coordinate for the column at the index c.
    // It will panic if c is out of bounds for the grid.
    X(c int) float64

    // Y returns the coordinate for the row at the index r.
    // It will panic if r is out of bounds for the grid.
    Y(r int) float64
}

type Flow

A Flow represents the amount of an entity flowing between two stocks.

type Flow struct {
    // SourceLabel and ReceptorLabel are the labels
    // of the stocks that originate and receive the flow,
    // respectively.
    SourceLabel, ReceptorLabel string

    // SourceCategory and ReceptorCategory define
    // the locations on the category axis of the stocks that
    // originate and receive the flow, respectively. The
    // SourceCategory must be a lower number than
    // the ReceptorCategory.
    SourceCategory, ReceptorCategory int

    // Value represents the magnitute of the flow.
    // It must be greater than or equal to zero.
    Value float64

    // Group specifies the group that a flow belongs
    // to. It is used in assigning styles to groups
    // and creating legends.
    Group string
}

type Function

Function implements the Plotter interface, drawing a line for the given function.

type Function struct {
    F func(x float64) (y float64)

    // XMin and XMax specify the range
    // of x values to pass to F.
    XMin, XMax float64

    Samples int

    draw.LineStyle
}

Example

ExampleFunction draws some functions.

Code:

quad := plotter.NewFunction(func(x float64) float64 { return x * x })
quad.Color = color.RGBA{B: 255, A: 255}

exp := plotter.NewFunction(func(x float64) float64 { return math.Pow(2, x) })
exp.Dashes = []vg.Length{vg.Points(2), vg.Points(2)}
exp.Width = vg.Points(2)
exp.Color = color.RGBA{G: 255, A: 255}

sin := plotter.NewFunction(func(x float64) float64 { return 10*math.Sin(x) + 50 })
sin.Dashes = []vg.Length{vg.Points(4), vg.Points(5)}
sin.Width = vg.Points(4)
sin.Color = color.RGBA{R: 255, A: 255}

p := plot.New()
p.Title.Text = "Functions"
p.X.Label.Text = "X"
p.Y.Label.Text = "Y"

p.Add(quad, exp, sin)
p.Legend.Add("x^2", quad)
p.Legend.Add("2^x", exp)
p.Legend.Add("10*sin(x)+50", sin)
p.Legend.ThumbnailWidth = 0.5 * vg.Inch

p.X.Min = 0
p.X.Max = 10
p.Y.Min = 0
p.Y.Max = 100

err := p.Save(200, 200, "testdata/functions.png")
if err != nil {
    log.Panic(err)
}

func NewFunction

func NewFunction(f func(float64) float64) *Function

NewFunction returns a Function that plots F using the default line style with 50 samples.

func (*Function) Plot

func (f *Function) Plot(c draw.Canvas, p *plot.Plot)

Plot implements the Plotter interface, drawing a line that connects each point in the Line.

func (Function) Thumbnail

func (f Function) Thumbnail(c *draw.Canvas)

Thumbnail draws a line in the given style down the center of a DrawArea as a thumbnail representation of the LineStyle of the function.

type GlyphBoxes

GlyphBoxes implements the Plotter interface, drawing all of the glyph boxes of the plot. This is intended for debugging.

type GlyphBoxes struct {
    draw.LineStyle
}

func NewGlyphBoxes

func NewGlyphBoxes() *GlyphBoxes

func (GlyphBoxes) Plot

func (g GlyphBoxes) Plot(c draw.Canvas, plt *plot.Plot)

type Grid

Grid implements the plot.Plotter interface, drawing a set of grid lines at the major tick marks.

type Grid struct {
    // Vertical is the style of the vertical lines.
    Vertical draw.LineStyle

    // Horizontal is the style of the horizontal lines.
    Horizontal draw.LineStyle
}

func NewGrid

func NewGrid() *Grid

NewGrid returns a new grid with both vertical and horizontal lines using the default grid line style.

func (*Grid) Plot

func (g *Grid) Plot(c draw.Canvas, plt *plot.Plot)

Plot implements the plot.Plotter interface.

type GridXYZ

GridXYZ describes three dimensional data where the X and Y coordinates are arranged on a rectangular grid.

type GridXYZ interface {
    // Dims returns the dimensions of the grid.
    Dims() (c, r int)

    // Z returns the value of a grid value at (c, r).
    // It will panic if c or r are out of bounds for the grid.
    Z(c, r int) float64

    // X returns the coordinate for the column at the index c.
    // It will panic if c is out of bounds for the grid.
    X(c int) float64

    // Y returns the coordinate for the row at the index r.
    // It will panic if r is out of bounds for the grid.
    Y(r int) float64
}

type HeatMap

HeatMap implements the Plotter interface, drawing a heat map of the values in the GridXYZ field.

type HeatMap struct {
    GridXYZ GridXYZ

    // Palette is the color palette used to render
    // the heat map. Palette must not be nil or
    // return a zero length []color.Color.
    Palette palette.Palette

    // Underflow and Overflow are colors used to fill
    // heat map elements outside the dynamic range
    // defined by Min and Max.
    Underflow color.Color
    Overflow  color.Color

    // NaN is the color used to fill heat map elements
    // that are NaN or do not map to a unique palette
    // color.
    NaN color.Color

    // Min and Max define the dynamic range of the
    // heat map.
    Min, Max float64

    // Rasterized indicates whether the heatmap
    // should be produced using raster-based drawing.
    Rasterized bool
}

Example

Code:

m := offsetUnitGrid{
    XOffset: -2,
    YOffset: -1,
    Data: mat.NewDense(3, 4, []float64{
        1, 2, 3, 4,
        5, 6, 7, 8,
        9, 10, 11, 12,
    })}
pal := palette.Heat(12, 1)
h := plotter.NewHeatMap(m, pal)

p := plot.New()
p.Title.Text = "Heat map"

p.X.Tick.Marker = integerTicks{}
p.Y.Tick.Marker = integerTicks{}

p.Add(h)

// Create a legend.
l := plot.NewLegend()
thumbs := plotter.PaletteThumbnailers(pal)
for i := len(thumbs) - 1; i >= 0; i-- {
    t := thumbs[i]
    if i != 0 && i != len(thumbs)-1 {
        l.Add("", t)
        continue
    }
    var val float64
    switch i {
    case 0:
        val = h.Min
    case len(thumbs) - 1:
        val = h.Max
    }
    l.Add(fmt.Sprintf("%.2g", val), t)
}

p.X.Padding = 0
p.Y.Padding = 0
p.X.Max = 1.5
p.Y.Max = 1.5

img := vgimg.New(250, 175)
dc := draw.New(img)

l.Top = true
// Calculate the width of the legend.
r := l.Rectangle(dc)
legendWidth := r.Max.X - r.Min.X
l.YOffs = -p.Title.TextStyle.FontExtents().Height // Adjust the legend down a little.

l.Draw(dc)
dc = draw.Crop(dc, 0, -legendWidth-vg.Millimeter, 0, 0) // Make space for the legend.
p.Draw(dc)
w, err := os.Create("testdata/heatMap.png")
if err != nil {
    log.Panic(err)
}
png := vgimg.PngCanvas{Canvas: img}
if _, err = png.WriteTo(w); err != nil {
    log.Panic(err)
}

Example (Rasterized)

Code:

m := offsetUnitGrid{
    XOffset: -2,
    YOffset: -1,
    Data: mat.NewDense(3, 4, []float64{
        1, 2, 3, 4,
        5, 6, 7, 8,
        9, 10, 11, 12,
    })}

pal := palette.Heat(12, 1)
plt := plot.New()

raster := plotter.NewHeatMap(&m, pal)
raster.Rasterized = true
plt.Add(raster)

err := plt.Save(250, 175, "testdata/rasterHeatMap.png")
if err != nil {
    log.Panic(err)
}

func NewHeatMap

func NewHeatMap(g GridXYZ, p palette.Palette) *HeatMap

NewHeatMap creates as new heat map plotter for the given data, using the provided palette. If g has Min and Max methods that return a float, those returned values are used to set the respective HeatMap fields. If the returned HeatMap is used when Min is greater than Max, the Plot method will panic.

func (*HeatMap) DataRange

func (h *HeatMap) DataRange() (xmin, xmax, ymin, ymax float64)

DataRange implements the DataRange method of the plot.DataRanger interface.

func (*HeatMap) GlyphBoxes

func (h *HeatMap) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox

GlyphBoxes implements the GlyphBoxes method of the plot.GlyphBoxer interface.

func (*HeatMap) Plot

func (h *HeatMap) Plot(c draw.Canvas, plt *plot.Plot)

Plot implements the Plot method of the plot.Plotter interface.

type Histogram

Histogram implements the Plotter interface, drawing a histogram of the data.

type Histogram struct {
    // Bins is the set of bins for this histogram.
    Bins []HistogramBin

    // Width is the width of each bin.
    Width float64

    // FillColor is the color used to fill each
    // bar of the histogram.  If the color is nil
    // then the bars are not filled.
    FillColor color.Color

    // LineStyle is the style of the outline of each
    // bar of the histogram.
    draw.LineStyle

    // LogY allows rendering with a log-scaled Y axis.
    // When enabled, histogram bins with no entries will be discarded from
    // the histogram's DataRange.
    // The lowest Y value for the DataRange will be corrected to leave an
    // arbitrary amount of height for the smallest bin entry so it is visible
    // on the final plot.
    LogY bool
}

Example

An example of making a histogram.

Code:

rnd := rand.New(rand.NewSource(1))

// stdNorm returns the probability of drawing a
// value from a standard normal distribution.
stdNorm := func(x float64) float64 {
    const sigma = 1.0
    const mu = 0.0
    const root2π = 2.50662827459517818309
    return 1.0 / (sigma * root2π) * math.Exp(-((x-mu)*(x-mu))/(2*sigma*sigma))
}

n := 10000
vals := make(plotter.Values, n)
for i := 0; i < n; i++ {
    vals[i] = rnd.NormFloat64()
}

p := plot.New()
p.Title.Text = "Histogram"
h, err := plotter.NewHist(vals, 16)
if err != nil {
    log.Panic(err)
}
h.Normalize(1)
p.Add(h)

// The normal distribution function
norm := plotter.NewFunction(stdNorm)
norm.Color = color.RGBA{R: 255, A: 255}
norm.Width = vg.Points(2)
p.Add(norm)

err = p.Save(200, 200, "testdata/histogram.png")
if err != nil {
    log.Panic(err)
}

Example (LogScaleY)

Code:

p := plot.New()
p.Title.Text = "Histogram in log-y"
p.Y.Scale = plot.LogScale{}
p.Y.Tick.Marker = plot.LogTicks{Prec: -1}
p.Y.Label.Text = "Y"
p.X.Label.Text = "X"

h1, err := plotter.NewHist(plotter.Values{
    -2, -2,
    -1,
    +3, +3, +3, +3,
    +1, +1, +1, +1, +1, +1, +1, +1, +1, +1,
    +1, +1, +1, +1, +1, +1, +1, +1, +1, +1,
}, 16)
if err != nil {
    log.Fatal(err)
}
h1.LogY = true
h1.FillColor = color.RGBA{255, 0, 0, 255}

h2, err := plotter.NewHist(plotter.Values{
    -3, -3, -3,
    +2, +2, +2, +2, +2,
}, 16)

if err != nil {
    log.Fatal(err)
}

h2.LogY = true
h2.FillColor = color.RGBA{0, 0, 255, 255}

p.Add(h1, h2, plotter.NewGrid())

err = p.Save(200, 200, "testdata/histogram_logy.png")
if err != nil {
    log.Fatal(err)
}

func NewHist

func NewHist(vs Valuer, n int) (*Histogram, error)

NewHist returns a new histogram, as in NewHistogram, except that it accepts a Valuer instead of an XYer.

func NewHistogram

func NewHistogram(xy XYer, n int) (*Histogram, error)

NewHistogram returns a new histogram that represents the distribution of values using the given number of bins.

Each y value is assumed to be the frequency count for the corresponding x.

If the number of bins is non-positive than a reasonable default is used.

func (*Histogram) DataRange

func (h *Histogram) DataRange() (xmin, xmax, ymin, ymax float64)

DataRange returns the minimum and maximum X and Y values

func (*Histogram) Normalize

func (h *Histogram) Normalize(sum float64)

Normalize normalizes the histogram so that the total area beneath it sums to a given value.

func (*Histogram) Plot

func (h *Histogram) Plot(c draw.Canvas, p *plot.Plot)

Plot implements the Plotter interface, drawing a line that connects each point in the Line.

func (*Histogram) Thumbnail

func (h *Histogram) Thumbnail(c *draw.Canvas)

Thumbnail draws a rectangle in the given style of the histogram.

type HistogramBin

A HistogramBin approximates the number of values within a range by a single number (the weight).

type HistogramBin struct {
    Min, Max float64
    Weight   float64
}

type Image

Image is a plotter that draws a scaled, raster image.

type Image struct {
    // contains filtered or unexported fields
}

Example

An example of embedding an image in a plot.

Code:

p := plot.New()
p.Title.Text = "A Logo"

// load an image
f, err := os.Open("testdata/image_plot_input.png")
if err != nil {
    log.Fatalf("error opening image file: %v\n", err)
}
defer f.Close()

img, err := png.Decode(f)
if err != nil {
    log.Fatalf("error decoding image file: %v\n", err)
}

p.Add(plotter.NewImage(img, 100, 100, 200, 200))

const (
    w = 5 * vg.Centimeter
    h = 5 * vg.Centimeter
)

err = p.Save(w, h, "testdata/image_plot.png")
if err != nil {
    log.Fatalf("error saving image plot: %v\n", err)
}

Example (Log)

An example of embedding an image in a plot with non-linear axes.

Code:

p := plot.New()
p.Title.Text = "A Logo"

// load an image
f, err := os.Open("testdata/gopher.png")
if err != nil {
    log.Fatalf("error opening image file: %v\n", err)
}
defer f.Close()

img, err := png.Decode(f)
if err != nil {
    log.Fatalf("error decoding image file: %v\n", err)
}

p.Add(plotter.NewImage(img, 100, 100, 10000, 10000))

// Transform axes.
p.X.Scale = plot.LogScale{}
p.Y.Scale = plot.LogScale{}
p.X.Tick.Marker = plot.LogTicks{Prec: -1}
p.Y.Tick.Marker = plot.LogTicks{Prec: 3}

const (
    w = 5 * vg.Centimeter
    h = 5 * vg.Centimeter
)

err = p.Save(w, h, "testdata/image_plot_log.png")
if err != nil {
    log.Fatalf("error saving image plot: %v\n", err)
}

Example (Uniform)

An example of embedding a uniform image in a plot.

Code:

p := plot.New()
p.Title.Text = "Uniform image"

img := image.NewUniform(color.RGBA{R: 90, G: 155, B: 212, A: 255})
p.Add(plotter.NewImage(img, 100, 100, 10000, 10000))

const (
    w = 5 * vg.Centimeter
    h = 5 * vg.Centimeter
)

err := p.Save(w, h, "testdata/image_plot_uniform.png")
if err != nil {
    log.Fatalf("error saving image plot: %v\n", err)
}

func NewImage

func NewImage(img image.Image, xmin, ymin, xmax, ymax float64) *Image

NewImage creates a new image plotter. Image will plot img inside the rectangle defined by the (xmin, ymin) and (xmax, ymax) points given in the data space. The img will be scaled to fit inside the rectangle.

func (*Image) DataRange

func (img *Image) DataRange() (xmin, xmax, ymin, ymax float64)

DataRange implements the DataRange method of the plot.DataRanger interface.

func (*Image) GlyphBoxes

func (img *Image) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox

GlyphBoxes implements the GlyphBoxes method of the plot.GlyphBoxer interface.

func (*Image) Plot

func (img *Image) Plot(c draw.Canvas, p *plot.Plot)

Plot implements the Plot method of the plot.Plotter interface.

type Labeller

Labeller wraps the Label methods.

type Labeller interface {
    // Label returns a label.
    Label(int) string
}

type Labels

Labels implements the Plotter interface, drawing a set of labels at specified points.

type Labels struct {
    XYs

    // Labels is the set of labels corresponding
    // to each point.
    Labels []string

    // TextStyle is the style of the label text. Each label
    // can have a different text style.
    TextStyle []text.Style

    // Offset is added directly to the final label location.
    Offset vg.Point
}

Example

Code:

p := plot.New()
p.Title.Text = "Labels"
p.X.Min = -10
p.X.Max = +10
p.Y.Min = 0
p.Y.Max = +20

labels, err := plotter.NewLabels(plotter.XYLabels{
    XYs: []plotter.XY{
        {X: -5, Y: 5},
        {X: +5, Y: 5},
        {X: +5, Y: 15},
        {X: -5, Y: 15},
    },
    Labels: []string{"A", "B", "C", "D"},
})
if err != nil {
    log.Fatalf("could not creates labels plotter: %+v", err)
}

p.Add(labels)

err = p.Save(10*vg.Centimeter, 10*vg.Centimeter, "testdata/labels.png")
if err != nil {
    log.Fatalf("could save plot: %+v", err)
}

Example (InCanvasCoordinates)

ExampleLabels_inCanvasCoordinates shows how to write a label in a plot using the canvas coordinates (instead of the data coordinates.) It can be useful in the situation where one wants to draw some label always in the same location of a plot, irrespective of the minute details of a particular plot data range.

Code:

p := plot.New()
p.Title.Text = "Labels - X"
p.X.Min = -10
p.X.Max = +10
p.Y.Min = 0
p.Y.Max = +20

labels, err := plotter.NewLabels(plotter.XYLabels{
    XYs: []plotter.XY{
        {X: -5, Y: 5},
        {X: +5, Y: 5},
        {X: +5, Y: 15},
        {X: -5, Y: 15},
    },
    Labels: []string{"A", "B", "C", "D"},
},
)
if err != nil {
    log.Fatalf("could not creates labels plotter: %+v", err)
}

p.Add(labels)

f, err := os.Create("testdata/labels_cnv_coords.png")
if err != nil {
    log.Fatalf("could not create output plot file: %+v", err)
}
defer f.Close()

cnv := vgimg.PngCanvas{
    Canvas: vgimg.New(10*vg.Centimeter, 10*vg.Centimeter),
}

dc := draw.New(cnv)
p.Draw(dc)

// Put an 'X' in the middle of the data-canvas.
{
    fnt := p.TextHandler.Cache().Lookup(plotter.DefaultFont, vg.Points(12))
    da := p.DataCanvas(dc)
    da.FillString(fnt, vg.Point{X: da.X(0.5), Y: da.Y(0.5)}, "X")
}

_, err = cnv.WriteTo(f)
if err != nil {
    log.Fatalf("could not write to output plot file: %+v", err)
}

err = f.Close()
if err != nil {
    log.Fatalf("could save plot: %+v", err)
}

func NewLabels

func NewLabels(d XYLabeller) (*Labels, error)

NewLabels returns a new Labels using the DefaultFont and the DefaultFontSize.

func (*Labels) DataRange

func (l *Labels) DataRange() (xmin, xmax, ymin, ymax float64)

DataRange returns the minimum and maximum X and Y values

func (*Labels) GlyphBoxes

func (l *Labels) GlyphBoxes(p *plot.Plot) []plot.GlyphBox

GlyphBoxes returns a slice of GlyphBoxes, one for each of the labels, implementing the plot.GlyphBoxer interface.

func (*Labels) Plot

func (l *Labels) Plot(c draw.Canvas, p *plot.Plot)

Plot implements the Plotter interface, drawing labels.

type Line

Line implements the Plotter interface, drawing a line.

type Line struct {
    // XYs is a copy of the points for this line.
    XYs

    // StepStyle is the kind of the step line.
    StepStyle StepKind

    // LineStyle is the style of the line connecting the points.
    // Use zero width to disable lines.
    draw.LineStyle

    // FillColor is the color to fill the area below the plot.
    // Use nil to disable the filling. This is the default.
    FillColor color.Color
}

Example (FilledLine)

Code:

rnd := rand.New(rand.NewSource(1))

// randomPoints returns some random x, y points
// with some interesting kind of trend.
randomPoints := func(n int, x float64) plotter.XYs {
    pts := make(plotter.XYs, n)
    for i := range pts {
        if i == 0 {
            pts[i].X = x + rnd.Float64()
        } else {
            pts[i].X = pts[i-1].X + 0.5 + rnd.Float64()
        }
        pts[i].Y = -5. + 10*rnd.Float64()
    }
    return pts
}

p := plot.New()
p.Title.Text = "Filled Line Example"
p.X.Label.Text = "X"
p.Y.Label.Text = "Y"
p.Add(plotter.NewGrid())

filled, err := plotter.NewLine(randomPoints(4, 0))
if err != nil {
    log.Panic(err)
}
filled.FillColor = color.RGBA{R: 196, G: 255, B: 196, A: 255}

p.Add(filled)

err = p.Save(200, 200, "testdata/filledLine.png")
if err != nil {
    log.Panic(err)
}

Example (StepLine)

Code:

rnd := rand.New(rand.NewSource(1))

// randomPoints returns some random x, y points
// with some interesting kind of trend.
randomPoints := func(n int, x float64) plotter.XYs {
    pts := make(plotter.XYs, n)
    for i := range pts {
        pts[i].X = float64(i) + x
        pts[i].Y = 5. + 10*rnd.Float64()
    }
    return pts
}

n := 4

p := plot.New()
p.Title.Text = "Step Example"
p.X.Label.Text = "X"
p.Y.Label.Text = "Y"
p.Add(plotter.NewGrid())

stepPre, err := plotter.NewLine(randomPoints(n, 0))
if err != nil {
    log.Panic(err)
}
stepPre.StepStyle = plotter.PreStep
stepPre.FillColor = color.RGBA{R: 196, G: 255, B: 196, A: 255}

stepMid, err := plotter.NewLine(randomPoints(n, 3.5))
if err != nil {
    log.Panic(err)
}
stepMid.StepStyle = plotter.MidStep
stepMid.LineStyle = draw.LineStyle{Color: color.RGBA{R: 196, B: 128, A: 255}, Width: vg.Points(1)}

stepMidFilled, err := plotter.NewLine(randomPoints(n, 7))
if err != nil {
    log.Panic(err)
}
stepMidFilled.StepStyle = plotter.MidStep
stepMidFilled.LineStyle = draw.LineStyle{Color: color.RGBA{R: 196, B: 128, A: 255}, Width: vg.Points(1)}
stepMidFilled.FillColor = color.RGBA{R: 255, G: 196, B: 196, A: 255}

stepPost, err := plotter.NewLine(randomPoints(n, 10.5))
if err != nil {
    log.Panic(err)
}
stepPost.StepStyle = plotter.PostStep
stepPost.LineStyle.Width = 0
stepPost.FillColor = color.RGBA{R: 196, G: 196, B: 255, A: 255}

p.Add(stepPre, stepMid, stepMidFilled, stepPost)
p.Legend.Add("pre", stepPre)
p.Legend.Add("mid", stepMid)
p.Legend.Add("midFilled", stepMidFilled)
p.Legend.Add("post", stepPost)
p.Legend.Top = true
p.Y.Max = 20
p.Y.Min = 0

err = p.Save(200, 200, "testdata/step.png")
if err != nil {
    log.Panic(err)
}

func NewLine

func NewLine(xys XYer) (*Line, error)

NewLine returns a Line that uses the default line style and does not draw glyphs.

func (*Line) DataRange

func (pts *Line) DataRange() (xmin, xmax, ymin, ymax float64)

DataRange returns the minimum and maximum x and y values, implementing the plot.DataRanger interface.

func (*Line) GlyphBoxes

func (pts *Line) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox

GlyphBoxes implements the plot.GlyphBoxer interface.

func (*Line) Plot

func (pts *Line) Plot(c draw.Canvas, plt *plot.Plot)

Plot draws the Line, implementing the plot.Plotter interface.

func (*Line) Thumbnail

func (pts *Line) Thumbnail(c *draw.Canvas)

Thumbnail returns the thumbnail for the Line, implementing the plot.Thumbnailer interface.

type Polygon

Polygon implements the Plotter interface, drawing a polygon.

type Polygon struct {
    // XYs is a copy of the vertices of this polygon.
    // Each item in the array holds one ring in the
    // Polygon.
    XYs []XYs

    // LineStyle is the style of the line around the edge
    // of the polygon.
    draw.LineStyle

    // Color is the fill color of the polygon.
    Color color.Color
}

Example (Hexagons)

ExamplePolygon_hexagons creates a heat map with hexagon shapes. The output of this example is at https://github.com/gonum/plot/blob/master/plotter/testdata/polygon_hexagons_golden.png.

Code:

// hex returns a hexagon centered at (x,y) with radius r.
hex := func(x, y, r float64) plotter.XYs {
    g := make(plotter.XYs, 6)
    for i := range g {
        g[i].X = x + r*math.Cos(math.Pi/3*float64(i))
        g[i].Y = y + r*math.Sin(math.Pi/3*float64(i))
    }
    return g
}

var err error
p := plot.New()
p.Title.Text = "Hexagons"
p.X.Label.Text = "X"
p.Y.Label.Text = "Y"

colorMap := moreland.SmoothBlueRed()
colorMap.SetMax(2)
colorMap.SetMin(-2)
colorMap.SetConvergePoint(0)

const (
    r = math.Pi / 4 // r is the hexagon radius.
    // x0 and y0 are the beginning coordinates for the hexagon plot.
    x0 = 0.0
    y0 = 0.0
    // nx and ny are the number of hexagons in the x and y directions.
    nx = 5
    ny = 5
)
// dx and dy are the distance between hexgons.
dx := 3 * r
dy := r * math.Sqrt(3)

xstart := []float64{x0, x0 - 1.5*r}
ystart := []float64{y0, y0 - r}
for i, xmin := range xstart {
    ymin := ystart[i]
    x := xmin
    for ix := 0; ix < nx; ix++ {
        y := ymin
        for iy := 0; iy < ny; iy++ {
            var poly *plotter.Polygon
            poly, err = plotter.NewPolygon(hex(x, y, r))
            if err != nil {
                log.Panic(err)
            }
            poly.Color, err = colorMap.At(math.Sin(x) + math.Sin(y))
            if err != nil {
                log.Panic(err)
            }
            poly.LineStyle.Width = 0
            p.Add(poly)
            y += dy
        }
        x += dx
    }
}
if err = p.Save(100, 100, "testdata/polygon_hexagons.png"); err != nil {
    log.Panic(err)
}

Example (Holes)

ExamplePolygon_holes draws a polygon with holes, showing how the different built-in vg backends render polygons with holes. The output of this example is at https://github.com/gonum/plot/blob/master/plotter/testdata/polygon_holes_golden.png, https://github.com/gonum/plot/blob/master/plotter/testdata/polygon_holes_golden.svg, https://github.com/gonum/plot/blob/master/plotter/testdata/polygon_holes_golden.pdf, and https://github.com/gonum/plot/blob/master/plotter/testdata/polygon_holes_golden.eps.

Code:

// Create an outer ring.
outer1 := plotter.XYs{{X: 0, Y: 0}, {X: 4, Y: 0}, {X: 4, Y: 4}, {X: 0, Y: 4}}

// Create an inner ring with the same
// winding order as the outer ring.
inner1 := plotter.XYs{{X: 0.5, Y: 0.5}, {X: 1.5, Y: 0.5}, {X: 1.5, Y: 1.5}, {X: 0.5, Y: 1.5}}

// Create an inner polygon with the opposite
// winding order as the outer polygon.
inner2 := plotter.XYs{{X: 3.5, Y: 2.5}, {X: 2.5, Y: 2.5}, {X: 2.5, Y: 3.5}, {X: 3.5, Y: 3.5}}

poly, err := plotter.NewPolygon(outer1, inner1, inner2)
if err != nil {
    log.Panic(err)
}
poly.Color = color.NRGBA{B: 255, A: 255}

p := plot.New()
p.Title.Text = "Polygon with holes"
p.X.Label.Text = "X"
p.Y.Label.Text = "Y"
p.Add(poly)
p.Legend.Add("key", poly)
p.Legend.TextStyle.Font.Size = vg.Points(8)
p.Legend.TextStyle.Color = color.White
p.Legend.ThumbnailWidth = vg.Points(10)

// Here we save the image in different file formats
// to show how each back end handles polygon holes.

// The vgimg backend treats both internal polygons
// as holes by default.
err = p.Save(100, 100, "testdata/polygon_holes.png")
if err != nil {
    log.Panic(err)
}

// The vgsvg, vgpdf, and vgeps backgrounds all treat
// the internal polygon with the opposite winding
// direction as a hole but do not consider the internal
// polygon with the same winding direction to be a hole.
err = p.Save(100, 100, "testdata/polygon_holes.svg")
if err != nil {
    log.Panic(err)
}
err = p.Save(100, 100, "testdata/polygon_holes.pdf")
if err != nil {
    log.Panic(err)
}
err = p.Save(100, 100, "testdata/polygon_holes.eps")
if err != nil {
    log.Panic(err)
}

func NewPolygon

func NewPolygon(xys ...XYer) (*Polygon, error)

NewPolygon returns a polygon that uses the default line style and no fill color, where xys are the rings of the polygon. Different backends may render overlapping rings and self-intersections differently, but all built-in backends treat inner rings with the opposite winding order from the outer ring as holes.

func (*Polygon) DataRange

func (pts *Polygon) DataRange() (xmin, xmax, ymin, ymax float64)

DataRange returns the minimum and maximum x and y values, implementing the plot.DataRanger interface.

func (*Polygon) Plot

func (pts *Polygon) Plot(c draw.Canvas, plt *plot.Plot)

Plot draws the polygon, implementing the plot.Plotter interface.

func (*Polygon) Thumbnail

func (pts *Polygon) Thumbnail(c *draw.Canvas)

Thumbnail creates the thumbnail for the Polygon, implementing the plot.Thumbnailer interface.

type QuartPlot

QuartPlot implements the Plotter interface, drawing a plot to represent the distribution of values.

This style of the plot appears in Tufte's "The Visual Display of Quantitative Information".

type QuartPlot struct {

    // Offset is added to the x location of each plot.
    // When the Offset is zero, the plot is drawn
    // centered at its x location.
    Offset vg.Length

    // MedianStyle is the line style for the median point.
    MedianStyle draw.GlyphStyle

    // WhiskerStyle is the line style used to draw the
    // whiskers.
    WhiskerStyle draw.LineStyle

    // Horizontal dictates whether the QuartPlot should be in the vertical
    // (default) or horizontal direction.
    Horizontal bool
    // contains filtered or unexported fields
}

Example

Code:

rnd := rand.New(rand.NewSource(1))

// Create the example data.
n := 100
uniform := make(plotter.Values, n)
normal := make(plotter.Values, n)
expon := make(plotter.Values, n)
for i := 0; i < n; i++ {
    uniform[i] = rnd.Float64()
    normal[i] = rnd.NormFloat64()
    expon[i] = rnd.ExpFloat64()
}

// Create the QuartPlots
qp1, err := plotter.NewQuartPlot(0, uniform)
if err != nil {
    log.Panic(err)
}
qp2, err := plotter.NewQuartPlot(1, normal)
if err != nil {
    log.Panic(err)
}
qp3, err := plotter.NewQuartPlot(2, expon)
if err != nil {
    log.Panic(err)
}

// Create a vertical plot
p1 := plot.New()
p1.Title.Text = "Quartile Plot"
p1.Y.Label.Text = "plotter.Values"
p1.Add(qp1, qp2, qp3)

// Set the X axis of the plot to nominal with
// the given names for x=0, x=1 and x=2.
p1.NominalX("Uniform\nDistribution", "Normal\nDistribution",
    "Exponential\nDistribution")

err = p1.Save(200, 200, "testdata/verticalQuartPlot.png")
if err != nil {
    log.Panic(err)
}

// Create a horizontal plot
qp1.Horizontal = true
qp2.Horizontal = true
qp3.Horizontal = true

p2 := plot.New()
p2.Title.Text = "Quartile Plot"
p2.X.Label.Text = "plotter.Values"
p2.Add(qp1, qp2, qp3)

// Set the Y axis of the plot to nominal with
// the given names for y=0, y=1 and y=2.
p2.NominalY("Uniform\nDistribution", "Normal\nDistribution",
    "Exponential\nDistribution")

err = p2.Save(200, 200, "testdata/horizontalQuartPlot.png")
if err != nil {
    log.Panic(err)
}

// Now, create a grouped quartile plot.

p3 := plot.New()
p3.Title.Text = "Box Plot"
p3.Y.Label.Text = "plotter.Values"

w := vg.Points(10)
for x := 0.0; x < 3.0; x++ {
    b0, err := plotter.NewQuartPlot(x, uniform)
    if err != nil {
        log.Panic(err)
    }
    b0.Offset = -w
    b1, err := plotter.NewQuartPlot(x, normal)
    if err != nil {
        log.Panic(err)
    }
    b2, err := plotter.NewQuartPlot(x, expon)
    if err != nil {
        log.Panic(err)
    }
    b2.Offset = w
    p3.Add(b0, b1, b2)
}
p3.Add(plotter.NewGlyphBoxes())

p3.NominalX("Group 0", "Group 1", "Group 2")

err = p3.Save(200, 200, "testdata/groupedQuartPlot.png")
if err != nil {
    log.Panic(err)
}

func NewQuartPlot

func NewQuartPlot(loc float64, values Valuer) (*QuartPlot, error)

NewQuartPlot returns a new QuartPlot that represents the distribution of the given values.

An error is returned if the plot is created with no values.

The fence values are 1.5x the interquartile before the first quartile and after the third quartile. Any value that is outside of the fences are drawn as Outside points. The adjacent values (to which the whiskers stretch) are the minimum and maximum values that are not outside the fences.

func (*QuartPlot) DataRange

func (b *QuartPlot) DataRange() (float64, float64, float64, float64)

DataRange returns the minimum and maximum x and y values, implementing the plot.DataRanger interface.

func (*QuartPlot) GlyphBoxes

func (b *QuartPlot) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox

GlyphBoxes returns a slice of GlyphBoxes for the plot, implementing the plot.GlyphBoxer interface.

func (*QuartPlot) OutsideLabels

func (b *QuartPlot) OutsideLabels(labels Labeller) (*Labels, error)

OutsideLabels returns a *Labels that will plot a label for each of the outside points. The labels are assumed to correspond to the points used to create the plot.

func (*QuartPlot) Plot

func (b *QuartPlot) Plot(c draw.Canvas, plt *plot.Plot)

Plot draws the QuartPlot on Canvas c and Plot plt.

type Sankey

A Sankey diagram presents stock and flow data as rectangles representing the amount of each stock and lines between the stocks representing the amount of each flow.

type Sankey struct {
    // Color specifies the default fill
    // colors for the stocks and flows. If Color is not nil,
    // each stock and flow is rendered filled with Color,
    // otherwise no fill is performed. Colors can be
    // modified for individual stocks and flows.
    Color color.Color

    // StockBarWidth is the widths of the bars representing
    // the stocks. The default value is 15% larger than the
    // height of the stock label text.
    StockBarWidth vg.Length

    // LineStyle specifies the default border
    // line style for the stocks and flows. Styles can be
    // modified for individual stocks and flows.
    LineStyle draw.LineStyle

    // TextStyle specifies the default stock label
    // text style. Styles can be modified for
    // individual stocks.
    TextStyle text.Style

    // FlowStyle is a function that specifies the
    // background color and border line style of the
    // flow based on its group name. The default
    // function uses the default Color and LineStyle
    // specified above for all groups.
    FlowStyle func(group string) (color.Color, draw.LineStyle)

    // StockStyle is a function that specifies, for a stock
    // identified by its label and category, the label text
    // to be printed on the plot (lbl), the style of the text (ts),
    // the horizontal and vertical offsets for printing the text (xOff and yOff),
    // the color of the fill for the bar representing the stock (c),
    // and the style of the outline of the bar representing the stock (ls).
    // The default function uses the default TextStyle, color and LineStyle
    // specified above for all stocks; zero horizontal and vertical offsets;
    // and the stock label as the text to be printed on the plot.
    StockStyle func(label string, category int) (lbl string, ts text.Style, xOff, yOff vg.Length, c color.Color, ls draw.LineStyle)
    // contains filtered or unexported fields
}

Example (Grouped)

ExampleSankey_grouped creates a sankey diagram with grouped flows. The output can be found at https://github.com/gonum/plot/blob/master/plotter/testdata/sankeyGrouped_golden.png.

Code:

p := plot.New()
c := vgimg.New(vg.Points(300), vg.Points(180))
dc := draw.New(c)

// Define the stock categories
const (
    treeType int = iota
    consumer
    fate
)
categoryLabels := []string{"Tree type", "Consumer", "Fate"}

flows := []plotter.Flow{
    {
        SourceCategory:   treeType,
        SourceLabel:      "LargeLargeLargeLargeLargeLargeLargeLargeLarge",
        ReceptorCategory: consumer,
        ReceptorLabel:    "Mohamed",
        Group:            "Apples",
        Value:            5,
    },
    {
        SourceCategory:   treeType,
        SourceLabel:      "LargeLargeLargeLargeLargeLargeLargeLargeLarge",
        ReceptorCategory: consumer,
        ReceptorLabel:    "Mohamed",
        Group:            "Dates",
        Value:            3,
    },
    {
        SourceCategory:   treeType,
        SourceLabel:      "Small",
        ReceptorCategory: consumer,
        ReceptorLabel:    "Mohamed",
        Group:            "Lychees",
        Value:            2,
    },
    {
        SourceCategory:   treeType,
        SourceLabel:      "LargeLargeLargeLargeLargeLargeLargeLargeLarge",
        ReceptorCategory: consumer,
        ReceptorLabel:    "Sofia",
        Group:            "Apples",
        Value:            3,
    },
    {
        SourceCategory:   treeType,
        SourceLabel:      "LargeLargeLargeLargeLargeLargeLargeLargeLarge",
        ReceptorCategory: consumer,
        ReceptorLabel:    "Sofia",
        Group:            "Dates",
        Value:            4,
    },
    {
        SourceCategory:   treeType,
        SourceLabel:      "Small",
        ReceptorCategory: consumer,
        ReceptorLabel:    "Sofia",
        Group:            "Apples",
        Value:            1,
    },
    {
        SourceCategory:   treeType,
        SourceLabel:      "LargeLargeLargeLargeLargeLargeLargeLargeLarge",
        ReceptorCategory: consumer,
        ReceptorLabel:    "Wei",
        Group:            "Lychees",
        Value:            6,
    },
    {
        SourceCategory:   treeType,
        SourceLabel:      "Small",
        ReceptorCategory: consumer,
        ReceptorLabel:    "Wei",
        Group:            "Apples",
        Value:            3,
    },
    {
        SourceCategory:   consumer,
        SourceLabel:      "Mohamed",
        ReceptorCategory: fate,
        ReceptorLabel:    "Eaten",
        Group:            "Apples",
        Value:            4,
    },
    {
        SourceCategory:   consumer,
        SourceLabel:      "Mohamed",
        ReceptorCategory: fate,
        ReceptorLabel:    "Waste",
        Group:            "Apples",
        Value:            1,
    },
    {
        SourceCategory:   consumer,
        SourceLabel:      "Mohamed",
        ReceptorCategory: fate,
        ReceptorLabel:    "Eaten",
        Group:            "Dates",
        Value:            3,
    },
    {
        SourceCategory:   consumer,
        SourceLabel:      "Mohamed",
        ReceptorCategory: fate,
        ReceptorLabel:    "Waste",
        Group:            "Lychees",
        Value:            2,
    },
    {
        SourceCategory:   consumer,
        SourceLabel:      "Sofia",
        ReceptorCategory: fate,
        ReceptorLabel:    "Eaten",
        Group:            "Apples",
        Value:            4,
    },
    {
        SourceCategory:   consumer,
        SourceLabel:      "Sofia",
        ReceptorCategory: fate,
        ReceptorLabel:    "Eaten",
        Group:            "Dates",
        Value:            3,
    },
    {
        SourceCategory:   consumer,
        SourceLabel:      "Sofia",
        ReceptorCategory: fate,
        ReceptorLabel:    "Waste",
        Group:            "Dates",
        Value:            1,
    },
    {
        SourceCategory:   consumer,
        SourceLabel:      "Wei",
        ReceptorCategory: fate,
        ReceptorLabel:    "Eaten",
        Group:            "Lychees",
        Value:            6,
    },
    {
        SourceCategory:   consumer,
        SourceLabel:      "Wei",
        ReceptorCategory: fate,
        ReceptorLabel:    "Eaten",
        Group:            "Apples",
        Value:            2,
    },
    {
        SourceCategory:   consumer,
        SourceLabel:      "Wei",
        ReceptorCategory: fate,
        ReceptorLabel:    "Waste",
        Group:            "Apples",
        Value:            1,
    },
    {
        SourceCategory:   treeType,
        SourceLabel:      "LargeLargeLargeLargeLargeLargeLargeLargeLarge",
        ReceptorCategory: fate,
        ReceptorLabel:    "Waste",
        Group:            "Apples",
        Value:            1,
    },
    {
        SourceCategory:   treeType,
        SourceLabel:      "LargeLargeLargeLargeLargeLargeLargeLargeLarge",
        ReceptorCategory: fate,
        ReceptorLabel:    "Waste",
        Group:            "Dates",
        Value:            1,
    },
    {
        SourceCategory:   treeType,
        SourceLabel:      "Small",
        ReceptorCategory: fate,
        ReceptorLabel:    "Waste",
        Group:            "Lychees",
        Value:            0.3,
    },
}

sankey, err := plotter.NewSankey(flows...)
if err != nil {
    log.Panic(err)
}

// Here we specify the FLowStyle function to set the
// colors of the different fruit groups.
sankey.FlowStyle = func(group string) (color.Color, draw.LineStyle) {
    switch group {
    case "Lychees":
        return color.NRGBA{R: 242, G: 169, B: 178, A: 100}, sankey.LineStyle
    case "Apples":
        return color.NRGBA{R: 91, G: 194, B: 54, A: 100}, sankey.LineStyle
    case "Dates":
        return color.NRGBA{R: 112, G: 22, B: 0, A: 100}, sankey.LineStyle
    default:
        panic(fmt.Errorf("invalid group %s", group))
    }
}

// Here we set the StockStyle function to give an example of
// setting a custom style for one of the stocks.
sankey.StockStyle = func(label string, category int) (string, text.Style, vg.Length, vg.Length, color.Color, draw.LineStyle) {
    if label == "Small" && category == treeType {
        // Here we demonstrate how to rotate the label text
        // and change the style of the stock bar.
        ts := sankey.TextStyle
        ts.Rotation = 0.0
        ts.XAlign = draw.XRight
        ls := sankey.LineStyle
        ls.Color = color.White
        xOff := -sankey.StockBarWidth / 2
        yOff := vg.Length(0)
        return "small", ts, xOff, yOff, color.Black, ls
    }
    if label == "LargeLargeLargeLargeLargeLargeLargeLargeLarge" && category == treeType {
        // Here we demonstrate how to replace a long label that doesn't fit
        // in the existing space with a shorter version. Note that because
        // we are not able to account for the difference between the overall
        // canvas size and the size of the plotting area here, if a label
        // was only slightly larger than the available space, it would not
        // be caught and replaced.
        min, max, err := sankey.StockRange(label, category)
        if err != nil {
            log.Panic(err)
        }
        _, yTr := p.Transforms(&dc)
        barHeight := yTr(max) - yTr(min)
        if sankey.TextStyle.Width(label) > barHeight {
            return "large", sankey.TextStyle, 0, 0, color.White, sankey.LineStyle
        }
    }
    return label, sankey.TextStyle, 0, 0, color.White, sankey.LineStyle
}

p.Add(sankey)
p.Y.Label.Text = "Number of fruit pieces"
p.NominalX(categoryLabels...)

legendLabels, thumbs := sankey.Thumbnailers()
for i, l := range legendLabels {
    t := thumbs[i]
    p.Legend.Add(l, t)
}
p.Legend.Top = true
p.X.Max = 3.05 // give room for the legend

// Add boundary boxes for debugging.
p.Add(plotter.NewGlyphBoxes())

p.Draw(dc)
pngimg := vgimg.PngCanvas{Canvas: c}
f, err := os.Create("testdata/sankeyGrouped.png")
if err != nil {
    log.Panic(err)
}
if _, err = pngimg.WriteTo(f); err != nil {
    log.Panic(err)
}

Example (Simple)

ExampleSankey_sample creates a simple sankey diagram. The output can be found at https://github.com/gonum/plot/blob/master/plotter/testdata/sankeySimple_golden.png.

Code:

p := plot.New()

// Define the stock categories
const (
    treeType int = iota
    consumer
    fate
)
categoryLabels := []string{"Tree type", "Consumer", "Fate"}

flows := []plotter.Flow{
    {
        SourceCategory:   treeType,
        SourceLabel:      "Large",
        ReceptorCategory: consumer,
        ReceptorLabel:    "Mohamed",
        Value:            5,
    },
    {
        SourceCategory:   treeType,
        SourceLabel:      "Small",
        ReceptorCategory: consumer,
        ReceptorLabel:    "Mohamed",
        Value:            2,
    },
    {
        SourceCategory:   treeType,
        SourceLabel:      "Large",
        ReceptorCategory: consumer,
        ReceptorLabel:    "Sofia",
        Value:            3,
    },
    {
        SourceCategory:   treeType,
        SourceLabel:      "Small",
        ReceptorCategory: consumer,
        ReceptorLabel:    "Sofia",
        Value:            1,
    },
    {
        SourceCategory:   treeType,
        SourceLabel:      "Large",
        ReceptorCategory: consumer,
        ReceptorLabel:    "Wei",
        Value:            6,
    },
    {
        SourceCategory:   consumer,
        SourceLabel:      "Mohamed",
        ReceptorCategory: fate,
        ReceptorLabel:    "Eaten",
        Value:            6,
    },
    {
        SourceCategory:   consumer,
        SourceLabel:      "Mohamed",
        ReceptorCategory: fate,
        ReceptorLabel:    "Waste",
        Value:            1,
    },
    {
        SourceCategory:   consumer,
        SourceLabel:      "Sofia",
        ReceptorCategory: fate,
        ReceptorLabel:    "Eaten",
        Value:            3,
    },
    {
        SourceCategory:   consumer,
        SourceLabel:      "Sofia",
        ReceptorCategory: fate,
        ReceptorLabel:    "Waste",
        Value:            0.5, // An unbalanced flow
    },
    {
        SourceCategory:   consumer,
        SourceLabel:      "Wei",
        ReceptorCategory: fate,
        ReceptorLabel:    "Eaten",
        Value:            5,
    },
    {
        SourceCategory:   consumer,
        SourceLabel:      "Wei",
        ReceptorCategory: fate,
        ReceptorLabel:    "Waste",
        Value:            1,
    },
    {
        SourceCategory:   treeType,
        SourceLabel:      "Large",
        ReceptorCategory: fate,
        ReceptorLabel:    "Waste",
        Value:            1,
    },
    {
        SourceCategory:   treeType,
        SourceLabel:      "Small",
        ReceptorCategory: fate,
        ReceptorLabel:    "Waste",
        Value:            0.3,
    },
}

sankey, err := plotter.NewSankey(flows...)
if err != nil {
    log.Panic(err)
}
p.Add(sankey)
p.Y.Label.Text = "Number of apples"
p.NominalX(categoryLabels...)
err = p.Save(vg.Points(300), vg.Points(180), "testdata/sankeySimple.png")
if err != nil {
    log.Panic(err)
}

func NewSankey

func NewSankey(flows ...Flow) (*Sankey, error)

NewSankey creates a new Sankey diagram with the specified flows and stocks.

func (*Sankey) DataRange

func (s *Sankey) DataRange() (xmin, xmax, ymin, ymax float64)

DataRange implements the plot.DataRanger interface.

func (*Sankey) GlyphBoxes

func (s *Sankey) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox

GlyphBoxes implements the GlyphBoxer interface.

func (*Sankey) Plot

func (s *Sankey) Plot(c draw.Canvas, plt *plot.Plot)

Plot implements the plot.Plotter interface.

func (*Sankey) StockRange

func (s *Sankey) StockRange(label string, category int) (min, max float64, err error)

StockRange returns the minimum and maximum value on the value axis for the stock with the specified label and category.

func (*Sankey) Thumbnailers

func (s *Sankey) Thumbnailers() (legendLabels []string, thumbnailers []plot.Thumbnailer)

Thumbnailers creates a group of objects that can be used to add legend entries for the different flow groups in this diagram, as well as the flow group labels that correspond to them.

type Scatter

Scatter implements the Plotter interface, drawing a glyph for each of a set of points.

type Scatter struct {
    // XYs is a copy of the points for this scatter.
    XYs

    // GlyphStyleFunc, if not nil, specifies GlyphStyles
    // for individual points
    GlyphStyleFunc func(int) draw.GlyphStyle

    // GlyphStyle is the style of the glyphs drawn
    // at each point.
    draw.GlyphStyle
}

Example

ExampleScatter draws some scatter points, a line, and a line with points.

Code:

rnd := rand.New(rand.NewSource(1))

// randomPoints returns some random x, y points
// with some interesting kind of trend.
randomPoints := func(n int) plotter.XYs {
    pts := make(plotter.XYs, n)
    for i := range pts {
        if i == 0 {
            pts[i].X = rnd.Float64()
        } else {
            pts[i].X = pts[i-1].X + rnd.Float64()
        }
        pts[i].Y = pts[i].X + 10*rnd.Float64()
    }
    return pts
}

n := 15
scatterData := randomPoints(n)
lineData := randomPoints(n)
linePointsData := randomPoints(n)

p := plot.New()
p.Title.Text = "Points Example"
p.X.Label.Text = "X"
p.Y.Label.Text = "Y"
p.Add(plotter.NewGrid())

s, err := plotter.NewScatter(scatterData)
if err != nil {
    log.Panic(err)
}
s.GlyphStyle.Color = color.RGBA{R: 255, B: 128, A: 255}
s.GlyphStyle.Radius = vg.Points(3)

l, err := plotter.NewLine(lineData)
if err != nil {
    log.Panic(err)
}
l.LineStyle.Width = vg.Points(1)
l.LineStyle.Dashes = []vg.Length{vg.Points(5), vg.Points(5)}
l.LineStyle.Color = color.RGBA{B: 255, A: 255}

lpLine, lpPoints, err := plotter.NewLinePoints(linePointsData)
if err != nil {
    log.Panic(err)
}
lpLine.Color = color.RGBA{G: 255, A: 255}
lpPoints.Shape = draw.CircleGlyph{}
lpPoints.Color = color.RGBA{R: 255, A: 255}

p.Add(s, l, lpLine, lpPoints)
p.Legend.Add("scatter", s)
p.Legend.Add("line", l)
p.Legend.Add("line points", lpLine, lpPoints)

err = p.Save(200, 200, "testdata/scatter.png")
if err != nil {
    log.Panic(err)
}

Example (Bubbles)

ExampleScatter_bubbles draws some scatter points. Each point is plotted with a different radius size depending on external criteria.

Code:

rnd := rand.New(rand.NewSource(1))

// randomTriples returns some random but correlated x, y, z triples
randomTriples := func(n int) plotter.XYZs {
    data := make(plotter.XYZs, n)
    for i := range data {
        if i == 0 {
            data[i].X = rnd.Float64()
        } else {
            data[i].X = data[i-1].X + 2*rnd.Float64()
        }
        data[i].Y = data[i].X + 10*rnd.Float64()
        data[i].Z = data[i].X
    }
    return data
}

n := 10
scatterData := randomTriples(n)

// Calculate the range of Z values.
minZ, maxZ := math.Inf(1), math.Inf(-1)
for _, xyz := range scatterData {
    if xyz.Z > maxZ {
        maxZ = xyz.Z
    }
    if xyz.Z < minZ {
        minZ = xyz.Z
    }
}

p := plot.New()
p.Title.Text = "Bubbles"
p.X.Label.Text = "X"
p.Y.Label.Text = "Y"

sc, err := plotter.NewScatter(scatterData)
if err != nil {
    log.Panic(err)
}

// Specify style for individual points.
sc.GlyphStyleFunc = func(i int) draw.GlyphStyle {
    c := color.RGBA{R: 196, B: 128, A: 255}
    var minRadius, maxRadius = vg.Points(1), vg.Points(20)
    rng := maxRadius - minRadius
    _, _, z := scatterData.XYZ(i)
    d := (z - minZ) / (maxZ - minZ)
    r := vg.Length(d)*rng + minRadius
    return draw.GlyphStyle{Color: c, Radius: r, Shape: draw.CircleGlyph{}}
}
p.Add(sc)

err = p.Save(200, 200, "testdata/bubbles.png")
if err != nil {
    log.Panic(err)
}

Example (Color)

ExampleScatter_color draws a colored scatter plot. Each point is plotted with a different color depending on external criteria.

Code:

rnd := rand.New(rand.NewSource(1))

// randomTriples returns some random but correlated x, y, z triples
randomTriples := func(n int) plotter.XYZs {
    data := make(plotter.XYZs, n)
    for i := range data {
        if i == 0 {
            data[i].X = rnd.Float64()
        } else {
            data[i].X = data[i-1].X + 2*rnd.Float64()
        }
        data[i].Y = data[i].X + 10*rnd.Float64()
        data[i].Z = data[i].X
    }
    return data
}

n := 15
scatterData := randomTriples(n)

// Calculate the range of Z values.
minZ, maxZ := math.Inf(1), math.Inf(-1)
for _, xyz := range scatterData {
    if xyz.Z > maxZ {
        maxZ = xyz.Z
    }
    if xyz.Z < minZ {
        minZ = xyz.Z
    }
}

colors := moreland.Kindlmann() // Initialize a color map.
colors.SetMax(maxZ)
colors.SetMin(minZ)

p := plot.New()
p.Title.Text = "Colored Points Example"
p.X.Label.Text = "X"
p.Y.Label.Text = "Y"
p.Add(plotter.NewGrid())

sc, err := plotter.NewScatter(scatterData)
if err != nil {
    log.Panic(err)
}

// Specify style and color for individual points.
sc.GlyphStyleFunc = func(i int) draw.GlyphStyle {
    _, _, z := scatterData.XYZ(i)
    d := (z - minZ) / (maxZ - minZ)
    rng := maxZ - minZ
    k := d*rng + minZ
    c, err := colors.At(k)
    if err != nil {
        log.Panic(err)
    }
    return draw.GlyphStyle{Color: c, Radius: vg.Points(3), Shape: draw.CircleGlyph{}}
}
p.Add(sc)

//Create a legend
thumbs := plotter.PaletteThumbnailers(colors.Palette(n))
for i := len(thumbs) - 1; i >= 0; i-- {
    t := thumbs[i]
    if i != 0 && i != len(thumbs)-1 {
        p.Legend.Add("", t)
        continue
    }
    var val int
    switch i {
    case 0:
        val = int(minZ)
    case len(thumbs) - 1:
        val = int(maxZ)
    }
    p.Legend.Add(fmt.Sprintf("%d", val), t)
}

// This is the width of the legend, experimentally determined.
const legendWidth = vg.Centimeter

// Slide the legend over so it doesn't overlap the ScatterPlot.
p.Legend.XOffs = legendWidth

img := vgimg.New(300, 230)
dc := draw.New(img)
dc = draw.Crop(dc, 0, -legendWidth, 0, 0) // Make space for the legend.
p.Draw(dc)

w, err := os.Create("testdata/scatterColor.png")
if err != nil {
    log.Panic(err)
}
defer w.Close()
png := vgimg.PngCanvas{Canvas: img}
if _, err = png.WriteTo(w); err != nil {
    log.Panic(err)
}
if err = w.Close(); err != nil {
    log.Panic(err)
}

func NewScatter

func NewScatter(xys XYer) (*Scatter, error)

NewScatter returns a Scatter that uses the default glyph style.

func (*Scatter) DataRange

func (pts *Scatter) DataRange() (xmin, xmax, ymin, ymax float64)

DataRange returns the minimum and maximum x and y values, implementing the plot.DataRanger interface.

func (*Scatter) GlyphBoxes

func (pts *Scatter) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox

GlyphBoxes returns a slice of plot.GlyphBoxes, implementing the plot.GlyphBoxer interface.

func (*Scatter) Plot

func (pts *Scatter) Plot(c draw.Canvas, plt *plot.Plot)

Plot draws the Scatter, implementing the plot.Plotter interface.

func (*Scatter) Thumbnail

func (pts *Scatter) Thumbnail(c *draw.Canvas)

Thumbnail the thumbnail for the Scatter, implementing the plot.Thumbnailer interface.

type StepKind

StepKind specifies a form of a connection of two consecutive points.

type StepKind int
const (
    // NoStep connects two points by simple line
    NoStep StepKind = iota

    // PreStep connects two points by following lines: vertical, horizontal.
    PreStep

    // MidStep connects two points by following lines: horizontal, vertical, horizontal.
    // Vertical line is placed in the middle of the interval.
    MidStep

    // PostStep connects two points by following lines: horizontal, vertical.
    PostStep
)

type ValueLabels

ValueLabels implements both the Valuer and Labeller interfaces.

type ValueLabels []struct {
    Value float64
    Label string
}

func (ValueLabels) Label

func (vs ValueLabels) Label(i int) string

Label returns the label of item i.

func (ValueLabels) Len

func (vs ValueLabels) Len() int

Len returns the number of items.

func (ValueLabels) Value

func (vs ValueLabels) Value(i int) float64

Value returns the value of item i.

type Valuer

Valuer wraps the Len and Value methods.

type Valuer interface {
    // Len returns the number of values.
    Len() int

    // Value returns a value.
    Value(int) float64
}

type Values

Values implements the Valuer interface.

type Values []float64

func CopyValues

func CopyValues(vs Valuer) (Values, error)

CopyValues returns a Values that is a copy of the values from a Valuer, or an error if there are no values, or if one of the copied values is a NaN or Infinity.

func (Values) Len

func (vs Values) Len() int

func (Values) Value

func (vs Values) Value(i int) float64

type XErrorBars

XErrorBars implements the plot.Plotter, plot.DataRanger, and plot.GlyphBoxer interfaces, drawing horizontal error bars, denoting error in Y values.

type XErrorBars struct {
    XYs

    // XErrors is a copy of the X errors for each point.
    XErrors

    // LineStyle is the style used to draw the error bars.
    draw.LineStyle

    // CapWidth is the width of the caps drawn at the top
    // of each error bar.
    CapWidth vg.Length
}

func NewXErrorBars

func NewXErrorBars(xerrs interface {
    XYer
    XErrorer
}) (*XErrorBars, error)

Returns a new XErrorBars plotter, or an error on failure. The error values from the XErrorer interface are interpreted as relative to the corresponding X value. The errors for a given X value are computed by taking the absolute value of the error returned by the XErrorer and subtracting the first and adding the second to the X value.

func (*XErrorBars) DataRange

func (e *XErrorBars) DataRange() (xmin, xmax, ymin, ymax float64)

DataRange implements the plot.DataRanger interface.

func (*XErrorBars) GlyphBoxes

func (e *XErrorBars) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox

GlyphBoxes implements the plot.GlyphBoxer interface.

func (*XErrorBars) Plot

func (e *XErrorBars) Plot(c draw.Canvas, p *plot.Plot)

Plot implements the Plotter interface, drawing labels.

type XErrorer

XErrorer wraps the XError method.

type XErrorer interface {
    // XError returns two error values for X data.
    XError(int) (float64, float64)
}

type XErrors

XErrors implements the XErrorer interface.

type XErrors Errors

func (XErrors) XError

func (xe XErrors) XError(i int) (float64, float64)

type XValues

XValues implements the Valuer interface, returning the x value from an XYer.

type XValues struct {
    XYer
}

func (XValues) Value

func (xs XValues) Value(i int) float64

type XY

XY is an x and y value.

type XY struct{ X, Y float64 }

type XYLabeller

XYLabeller combines the XYer and Labeller types.

type XYLabeller interface {
    XYer
    Labeller
}

type XYLabels

XYLabels holds XY data with labels. The ith label corresponds to the ith XY.

type XYLabels struct {
    XYs
    Labels []string
}

func (XYLabels) Label

func (l XYLabels) Label(i int) string

Label returns the label for point index i.

type XYValues

XYValues implements the XYer interface, returning the x and y values from an XYZer.

type XYValues struct{ XYZer }

func (XYValues) XY

func (xy XYValues) XY(i int) (float64, float64)

XY implements the XY method of the XYer interface.

type XYZ

XYZ is an x, y and z value.

type XYZ struct{ X, Y, Z float64 }

type XYZer

XYZer wraps the Len and XYZ methods.

type XYZer interface {
    // Len returns the number of x, y, z triples.
    Len() int

    // XYZ returns an x, y, z triple.
    XYZ(int) (float64, float64, float64)

    // XY returns an x, y pair.
    XY(int) (float64, float64)
}

type XYZs

XYZs implements the XYZer interface using a slice.

type XYZs []XYZ

func CopyXYZs

func CopyXYZs(data XYZer) (XYZs, error)

CopyXYZs copies an XYZer.

func (XYZs) Len

func (xyz XYZs) Len() int

Len implements the Len method of the XYZer interface.

func (XYZs) XY

func (xyz XYZs) XY(i int) (float64, float64)

XY implements the XY method of the XYer interface.

func (XYZs) XYZ

func (xyz XYZs) XYZ(i int) (float64, float64, float64)

XYZ implements the XYZ method of the XYZer interface.

type XYer

XYer wraps the Len and XY methods.

type XYer interface {
    // Len returns the number of x, y pairs.
    Len() int

    // XY returns an x, y pair.
    XY(int) (x, y float64)
}

type XYs

XYs implements the XYer interface.

type XYs []XY

func CopyXYs

func CopyXYs(data XYer) (XYs, error)

CopyXYs returns an XYs that is a copy of the x and y values from an XYer, or an error if one of the data points contains a NaN or Infinity.

func (XYs) Len

func (xys XYs) Len() int

func (XYs) XY

func (xys XYs) XY(i int) (float64, float64)

type YErrorBars

YErrorBars implements the plot.Plotter, plot.DataRanger, and plot.GlyphBoxer interfaces, drawing vertical error bars, denoting error in Y values.

type YErrorBars struct {
    XYs

    // YErrors is a copy of the Y errors for each point.
    YErrors

    // LineStyle is the style used to draw the error bars.
    draw.LineStyle

    // CapWidth is the width of the caps drawn at the top
    // of each error bar.
    CapWidth vg.Length
}

func NewYErrorBars

func NewYErrorBars(yerrs interface {
    XYer
    YErrorer
}) (*YErrorBars, error)

NewYErrorBars returns a new YErrorBars plotter, or an error on failure. The error values from the YErrorer interface are interpreted as relative to the corresponding Y value. The errors for a given Y value are computed by taking the absolute value of the error returned by the YErrorer and subtracting the first and adding the second to the Y value.

func (*YErrorBars) DataRange

func (e *YErrorBars) DataRange() (xmin, xmax, ymin, ymax float64)

DataRange implements the plot.DataRanger interface.

func (*YErrorBars) GlyphBoxes

func (e *YErrorBars) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox

GlyphBoxes implements the plot.GlyphBoxer interface.

func (*YErrorBars) Plot

func (e *YErrorBars) Plot(c draw.Canvas, p *plot.Plot)

Plot implements the Plotter interface, drawing labels.

type YErrorer

YErrorer wraps the YError method.

type YErrorer interface {
    // YError returns two error values for Y data.
    YError(int) (float64, float64)
}

type YErrors

YErrors implements the YErrorer interface.

type YErrors Errors

func (YErrors) YError

func (ye YErrors) YError(i int) (float64, float64)

type YValues

YValues implements the Valuer interface, returning the y value from an XYer.

type YValues struct {
    XYer
}

func (YValues) Value

func (ys YValues) Value(i int) float64