// New returns a new plot with some reasonable // default settings. func New() (*Plot, error) { titleFont, err := vg.MakeFont(DefaultFont, 12) if err != nil { return nil, err } x, err := makeAxis() if err != nil { return nil, err } y, err := makeAxis() if err != nil { return nil, err } legend, err := makeLegend() if err != nil { return nil, err } p := &Plot{ BackgroundColor: color.White, X: x, Y: y, Legend: legend, } p.Title.TextStyle = draw.TextStyle{ Color: color.Black, Font: titleFont, } return p, nil }
// NewLabels returns a new Labels using the DefaultFont and // the DefaultFontSize. func NewLabels(d interface { XYer Labeller }) (*Labels, error) { xys, err := CopyXYs(d) if err != nil { return nil, err } if d.Len() != len(xys) { return nil, errors.New("Number of points does not match the number of labels") } strs := make([]string, d.Len()) for i := range strs { strs[i] = d.Label(i) } fnt, err := vg.MakeFont(DefaultFont, DefaultFontSize) if err != nil { return nil, err } return &Labels{ XYs: xys, Labels: strs, TextStyle: draw.TextStyle{Font: fnt}, }, nil }
// makeLegend returns a legend with the default // parameter settings. func makeLegend() (Legend, error) { font, err := vg.MakeFont(DefaultFont, vg.Points(12)) if err != nil { return Legend{}, err } return Legend{ ThumbnailWidth: vg.Points(20), TextStyle: draw.TextStyle{Font: font}, }, nil }
func main() { rna, err := readJSON(in) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } minLength, maxLength, binLength = rna.Min, rna.Max, rna.Bin p, err := plot.New() if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } mm, lo, hi, err := mouseTracks(rna.Features, highlight, palname, 15*vg.Centimeter, rna.Min-rna.Max) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } p.Add(mm...) p.HideAxes() font, err := vg.MakeFont("Helvetica", 14) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } n := rna.Sample[:strings.Index(rna.Sample, filepath.Ext(rna.Sample))] p.Title.Text = fmt.Sprintf( `%s %s min base quality: %v, minimum mapping score: %d minimum identity: %d%% length range: [%d,%d] heat range: [%f,%f]`, decorate(n, format, rna.Filter), rna.Sample, rna.MinQ, rna.MapQ, rna.MinID, rna.Min, rna.Max, lo, hi) p.Title.TextStyle = draw.TextStyle{Color: color.Gray{0}, Font: font} err = p.Save(19*vg.Centimeter, 25*vg.Centimeter, decorate(filepath.Base(rna.Sample), format, rna.Filter), ) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } }
func (text GrobText) Font() vg.Font { fname := text.font if fname == "" { fname = "Courier-Bold" } font, err := vg.MakeFont(fname, vg.Length(text.size)) if err != nil { panic(err.Error()) } return font }
// makeAxis returns a default Axis. // // The default range is (∞, ∞), and thus any finite // value is less than Min and greater than Max. func makeAxis() (Axis, error) { labelFont, err := vg.MakeFont(DefaultFont, vg.Points(12)) if err != nil { return Axis{}, err } tickFont, err := vg.MakeFont(DefaultFont, vg.Points(10)) if err != nil { return Axis{}, err } a := Axis{ Min: math.Inf(1), Max: math.Inf(-1), LineStyle: draw.LineStyle{ Color: color.Black, Width: vg.Points(0.5), }, Padding: vg.Points(5), Scale: LinearScale{}, } a.Label.TextStyle = draw.TextStyle{ Color: color.Black, Font: labelFont, } a.Tick.Label = draw.TextStyle{ Color: color.Black, Font: tickFont, } a.Tick.LineStyle = draw.LineStyle{ Color: color.Black, Width: vg.Points(0.5), } a.Tick.Length = vg.Points(8) a.Tick.Marker = DefaultTicks{} return a, nil }
func main() { bf, err := readBED(in) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } p, err := plot.New() if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } hs, err := tracks(scoreFeatures(bf, binLength, hg19.Chromosomes), 15*vg.Centimeter) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } p.Add(hs...) p.HideAxes() font, err := vg.MakeFont("Helvetica", 14) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } p.Title.Text = filepath.Base(in) p.Title.TextStyle = draw.TextStyle{Color: color.Gray{0}, Font: font} err = p.Save(19*vg.Centimeter, 25*vg.Centimeter, filepath.Base(in)+"."+format) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } }
// ReplayOn applies the set of Actions recorded by the Recorder onto // the destination Canvas. func (c *Canvas) ReplayOn(dst vg.Canvas) error { if c.fonts == nil { c.fonts = make(map[fontID]vg.Font) } for _, a := range c.Actions { fa, ok := a.(*FillString) if !ok { continue } f := fontID{name: fa.Font, size: fa.Size} if _, exists := c.fonts[f]; !exists { var err error c.fonts[f], err = vg.MakeFont(fa.Font, fa.Size) if err != nil { return err } } fa.fonts = c.fonts } for _, a := range c.Actions { a.ApplyTo(dst) } return nil }
func tracks(scores []rings.Scorer, diameter vg.Length) ([]plot.Plotter, error) { var p []plot.Plotter radius := diameter / 2 // Relative sizes. const ( gap = 0.005 label = 117. / 110. countsInner = 97. / 110. countsOuter = 70. / 110. karyotypeInner = 100. / 110. karyotypeOuter = 1. large = 6. / 110. small = 2. / 110. ) sty := plotter.DefaultLineStyle sty.Width /= 2 chr := make([]feat.Feature, len(hg19.Chromosomes)) for i, c := range hg19.Chromosomes { chr[i] = c } hs, err := rings.NewGappedBlocks( chr, rings.Arc{rings.Complete / 4 * rings.CounterClockwise, rings.Complete * rings.Clockwise}, radius*karyotypeInner, radius*karyotypeOuter, gap, ) if err != nil { return nil, err } hs.LineStyle = sty p = append(p, hs) bands := make([]feat.Feature, len(hg19.Bands)) cens := make([]feat.Feature, 0, len(hg19.Chromosomes)) for i, b := range hg19.Bands { bands[i] = colorBand{b} s := b.Start() // This condition depends on p -> q sort order in the $karyotype.Bands variable. // All standard genome packages follow this, though here the test is more general than // actually required since hs is telocentric. if b.Band[0] == 'q' && (s == 0 || hg19.Bands[i-1].Band[0] == 'p') { cens = append(cens, colorBand{&genome.Band{Band: "cen", Desc: "Band", StartPos: s, EndPos: s, Giemsa: "acen", Chr: b.Location()}}) } } b, err := rings.NewBlocks(bands, hs, radius*karyotypeInner, radius*karyotypeOuter) if err != nil { return nil, fmt.Errorf("bands: %v", err) } p = append(p, b) c, err := rings.NewBlocks(cens, hs, radius*karyotypeInner, radius*karyotypeOuter) if err != nil { return nil, fmt.Errorf("centromeres: %v", err) } p = append(p, c) font, err := vg.MakeFont("Helvetica", radius*large) if err != nil { return nil, err } lb, err := rings.NewLabels(hs, radius*label, rings.NameLabels(hs.Set)...) if err != nil { return nil, err } lb.TextStyle = draw.TextStyle{Color: color.Gray16{0}, Font: font} p = append(p, lb) smallFont, err := vg.MakeFont("Helvetica", radius*small) if err != nil { return nil, err } counts := make([]rings.Scorer, len(scores)) for i, s := range scores { counts[i] = s.(*feature) } ct, err := rings.NewScores(counts, hs, radius*countsInner, radius*countsOuter, &rings.Trace{ LineStyles: func() []draw.LineStyle { ls := []draw.LineStyle{sty} ls[0].Color = color.Gray16{0} return ls }(), Join: true, Axis: &rings.Axis{ Angle: rings.Complete / 4, Grid: plotter.DefaultGridLineStyle, LineStyle: sty, Tick: rings.TickConfig{ Marker: plot.DefaultTicks{}, LineStyle: sty, Length: 2, Label: draw.TextStyle{Color: color.Gray16{0}, Font: smallFont}, }, }, }, ) if err != nil { return nil, err } p = append(p, ct) return p, nil }
func TestLegendAlignment(t *testing.T) { font, err := vg.MakeFont(plot.DefaultFont, 10.822510822510822) // This font size gives an entry height of 10. if err != nil { t.Fatalf("failed to create font: %v", err) } l := plot.Legend{ ThumbnailWidth: vg.Points(20), TextStyle: draw.TextStyle{Font: font}, } for _, n := range []string{"A", "B", "C", "D"} { b, err := plotter.NewBarChart(plotter.Values{0}, 1) if err != nil { t.Fatalf("failed to create bar chart %q: %v", n, err) } l.Add(n, b) } r := recorder.New(100) c := draw.NewCanvas(r, 100, 100) l.Draw(draw.Canvas{ Canvas: c.Canvas, Rectangle: draw.Rectangle{ Min: draw.Point{0, 0}, Max: draw.Point{100, 100}, }, }) got := r.Actions // want is a snapshot of the actions for the code above when the // graphical output has been visually confirmed to be correct for // the bar charts example show in gonum/plot#25. want := []recorder.Action{ &recorder.SetColor{ Color: color.Gray16{}, }, &recorder.Fill{ Path: vg.Path{ {Type: vg.MoveComp, X: 80, Y: 30}, {Type: vg.LineComp, X: 80, Y: 40}, {Type: vg.LineComp, X: 100, Y: 40}, {Type: vg.LineComp, X: 100, Y: 30}, {Type: vg.CloseComp}, }, }, &recorder.SetColor{ Color: color.Gray16{}, }, &recorder.SetLineWidth{ Width: 1, }, &recorder.SetLineDash{}, &recorder.Stroke{ Path: vg.Path{ {Type: vg.MoveComp, X: 80, Y: 30}, {Type: vg.LineComp, X: 80, Y: 40}, {Type: vg.LineComp, X: 100, Y: 40}, {Type: vg.LineComp, X: 100, Y: 30}, {Type: vg.LineComp, X: 80, Y: 30}, }, }, &recorder.SetColor{}, &recorder.FillString{ Font: string("Times-Roman"), Size: 10.822510822510822, X: 69.48051948051948, Y: 30.82251082251082, String: "A", }, &recorder.SetColor{ Color: color.Gray16{}, }, &recorder.Fill{ Path: vg.Path{ {Type: vg.MoveComp, X: 80, Y: 20}, {Type: vg.LineComp, X: 80, Y: 30}, {Type: vg.LineComp, X: 100, Y: 30}, {Type: vg.LineComp, X: 100, Y: 20}, {Type: vg.CloseComp}, }, }, &recorder.SetColor{ Color: color.Gray16{}, }, &recorder.SetLineWidth{ Width: 1, }, &recorder.SetLineDash{}, &recorder.Stroke{ Path: vg.Path{ {Type: vg.MoveComp, X: 80, Y: 20}, {Type: vg.LineComp, X: 80, Y: 30}, {Type: vg.LineComp, X: 100, Y: 30}, {Type: vg.LineComp, X: 100, Y: 20}, {Type: vg.LineComp, X: 80, Y: 20}, }, }, &recorder.SetColor{}, &recorder.FillString{ Font: string("Times-Roman"), Size: 10.822510822510822, X: 70.07575757575758, Y: 20.82251082251082, String: "B", }, &recorder.SetColor{ Color: color.Gray16{ Y: uint16(0), }, }, &recorder.Fill{ Path: vg.Path{ {Type: vg.MoveComp, X: 80, Y: 10}, {Type: vg.LineComp, X: 80, Y: 20}, {Type: vg.LineComp, X: 100, Y: 20}, {Type: vg.LineComp, X: 100, Y: 10}, {Type: vg.CloseComp}, }, }, &recorder.SetColor{ Color: color.Gray16{}, }, &recorder.SetLineWidth{ Width: 1, }, &recorder.SetLineDash{}, &recorder.Stroke{ Path: vg.Path{ {Type: vg.MoveComp, X: 80, Y: 10}, {Type: vg.LineComp, X: 80, Y: 20}, {Type: vg.LineComp, X: 100, Y: 20}, {Type: vg.LineComp, X: 100, Y: 10}, {Type: vg.LineComp, X: 80, Y: 10}, }, }, &recorder.SetColor{}, &recorder.FillString{ Font: string("Times-Roman"), Size: 10.822510822510822, X: 70.07575757575758, Y: 10.822510822510822, String: "C", }, &recorder.SetColor{ Color: color.Gray16{}, }, &recorder.Fill{ Path: vg.Path{ {Type: vg.MoveComp, X: 80, Y: 0}, {Type: vg.LineComp, X: 80, Y: 10}, {Type: vg.LineComp, X: 100, Y: 10}, {Type: vg.LineComp, X: 100, Y: 0}, {Type: vg.CloseComp}, }, }, &recorder.SetColor{ Color: color.Gray16{}, }, &recorder.SetLineWidth{ Width: 1, }, &recorder.SetLineDash{}, &recorder.Stroke{ Path: vg.Path{ {Type: vg.MoveComp, X: 80, Y: 0}, {Type: vg.LineComp, X: 80, Y: 10}, {Type: vg.LineComp, X: 100, Y: 10}, {Type: vg.LineComp, X: 100, Y: 0}, {Type: vg.LineComp, X: 80, Y: 0}, }, }, &recorder.SetColor{}, &recorder.FillString{ Font: string("Times-Roman"), Size: 10.822510822510822, X: 69.48051948051948, Y: 0.8225108225108215, String: "D", }, } if !reflect.DeepEqual(got, want) { t.Errorf("unexpected legend actions:\ngot:\n%s\nwant:\n%s", formatActions(got), formatActions(want)) } }
func mouseTracks(scores []rings.Scorer, highlight []string, palname string, diameter vg.Length, lenRange int) (pp []plot.Plotter, lo, hi float64, err error) { var p []plot.Plotter radius := diameter / 2 // Relative sizes. const ( gap = 0.005 label = 117. / 110. countsInner = 25. / 110. countsOuter = 40. / 110. heatInner = 45. / 110. heatOuter = 75. / 110. traceInner = 80. / 110. traceOuter = 95. / 110. karyotypeInner = 100. / 110. karyotypeOuter = 1. large = 7. / 110. small = 2. / 110. ) sty := plotter.DefaultLineStyle sty.Width /= 2 chr := make([]feat.Feature, len(mm10.Chromosomes)) for i, c := range mm10.Chromosomes { chr[i] = c } mm, err := rings.NewGappedBlocks( chr, rings.Arc{rings.Complete / 4 * rings.CounterClockwise, rings.Complete * rings.Clockwise}, radius*karyotypeInner, radius*karyotypeOuter, gap, ) if err != nil { return nil, 0, 0, err } mm.LineStyle = sty pal, err := brewer.GetPalette(brewer.TypeQualitative, palname, len(highlight)) if err == nil { for i, hn := range highlight { for _, c := range mm.Set { if hn == strings.ToLower(c.Name()) { arc, err := mm.Base.ArcOf(c, nil) arc.Theta += rings.Complete * gap / 2 arc.Phi -= rings.Complete * gap if err != nil { fmt.Printf("could not find: %s\n", hn) break } col := pal.Colors()[i] nc := color.NRGBAModel.Convert(col).(color.NRGBA) nc.A = 0x40 h := rings.NewHighlight( nc, arc, radius*(traceInner-2.5/110.), radius*(label+5./110.), ) h.LineStyle = sty h.LineStyle.Width /= 4 p = append(p, h) break } } } } else if len(highlight) > 0 { fmt.Println("no palette") } p = append(p, mm) bands := make([]feat.Feature, len(mm10.Bands)) cens := make([]feat.Feature, 0, len(mm10.Chromosomes)) for i, b := range mm10.Bands { bands[i] = colorBand{b} s := b.Start() // This condition depends on p -> q sort order in the $karyotype.Bands variable. // All standard genome packages follow this, though here the test is more general than // actually required since mm is telocentric. if b.Band[0] == 'q' && (s == 0 || mm10.Bands[i-1].Band[0] == 'p') { cens = append(cens, colorBand{&genome.Band{Band: "cen", Desc: "Band", StartPos: s, EndPos: s, Giemsa: "acen", Chr: b.Location()}}) } } b, err := rings.NewBlocks(bands, mm, radius*karyotypeInner, radius*karyotypeOuter) if err != nil { return nil, 0, 0, fmt.Errorf("bands: %v", err) } p = append(p, b) c, err := rings.NewBlocks(cens, mm, radius*karyotypeInner, radius*karyotypeOuter) if err != nil { return nil, 0, 0, fmt.Errorf("centromeres: %v", err) } p = append(p, c) font, err := vg.MakeFont("Helvetica", radius*large) if err != nil { return nil, 0, 0, err } lb, err := rings.NewLabels(mm, radius*label, rings.NameLabels(mm.Set)...) if err != nil { return nil, 0, 0, err } lb.TextStyle = draw.TextStyle{Color: color.Gray16{0}, Font: font} p = append(p, lb) s, err := rings.NewScores(scores, mm, radius*heatInner, radius*heatOuter, &rings.Heat{Palette: palette.Heat(10, 1).Colors()}, ) if err != nil { return nil, 0, 0, err } p = append(p, s) smallFont, err := vg.MakeFont("Helvetica", radius*small) if err != nil { return nil, 0, 0, err } traces := make([]rings.Scorer, len(scores)) for i, s := range scores { traces[i] = &tfs{s.(*feature)} } t, err := rings.NewScores(traces, mm, radius*traceInner, radius*traceOuter, &rings.Trace{ LineStyles: func() []draw.LineStyle { ls := []draw.LineStyle{sty, sty} for i, c := range brewer.Set1[3].Colors()[:len(ls)] { nc := color.NRGBAModel.Convert(c).(color.NRGBA) nc.A = 0x80 ls[i].Color = nc } return ls }(), Join: true, Axis: &rings.Axis{ Angle: rings.Complete / 4, Grid: plotter.DefaultGridLineStyle, LineStyle: sty, Tick: rings.TickConfig{ Marker: plot.DefaultTicks{}, LineStyle: sty, Length: 2, Label: draw.TextStyle{Color: color.Gray16{0}, Font: smallFont}, }, }, }, ) if err != nil { return nil, 0, 0, err } if maxTrace != 0 { t.Max = maxTrace if t.Min > t.Max { return nil, 0, 0, fmt.Errorf("maximum trace out of range: min=%f", t.Min) } } if !math.IsInf(t.Max-t.Min, 0) { p = append(p, t) } counts := make([]rings.Scorer, len(scores)) for i, s := range scores { counts[i] = ctfs{s.(*feature)} } ct, err := rings.NewScores(counts, mm, radius*countsInner, radius*countsOuter, &rings.Trace{ LineStyles: func() []draw.LineStyle { ls := []draw.LineStyle{sty} ls[0].Color = color.Gray16{0} return ls }(), Join: true, Axis: &rings.Axis{ Angle: rings.Complete / 4, Grid: plotter.DefaultGridLineStyle, LineStyle: sty, Tick: rings.TickConfig{ Marker: plot.DefaultTicks{}, LineStyle: sty, Length: 2, Label: draw.TextStyle{Color: color.Gray16{0}, Font: smallFont}, }, }, }, ) if err != nil { return nil, 0, 0, err } if maxCounts != 0 { ct.Max = maxCounts if ct.Min > ct.Max { return nil, 0, 0, fmt.Errorf("maximum counts out of range: min=%f", ct.Min) } } p = append(p, ct) return p, s.Min, s.Max, nil }
func main() { rna, err := readJSON(in) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } minLength, maxLength, binLength = rna.Min, rna.Max, rna.Bin switch normalisation { case 0: weightFactors[0], weightFactors[1] = float64(rna.Totals[0]), float64(rna.Totals[1]) case 1: weightFactors, err = normaliseByBin(rna) case 2: weightFactors, err = normaliseByBlock(rna) default: err = errors.New("illegal normalisation strategy") } if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } p, err := plot.New() if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } mm, lo, hi, err := mouseTracks(rna.Features, highlight, palname, 15*vg.Centimeter, rna.Min-rna.Max) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } p.Add(mm...) p.HideAxes() font, err := vg.MakeFont("Helvetica", 14) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } for i, n := range rna.Pair { n = filepath.Base(n) rna.Pair[i] = n[:strings.Index(n, filepath.Ext(n))] } p.Title.Text = fmt.Sprintf( `%s %s %s min base quality: %v, minimum mapping score: %d minimum identity: %d%% length range: [%d,%d] heat range: [%f,%f]`, decorate(rna.Pair[1], format, rna.Filter), rna.Classes, rna.Pair, rna.MinQ, rna.MapQ, rna.MinID, rna.Min, rna.Max, lo, hi) p.Title.TextStyle = draw.TextStyle{Color: color.Gray{0}, Font: font} for i, class := range rna.Classes { rna.Classes[i] = class[strings.LastIndex(class, "/")+1:] } var classes string if len(rna.Classes) > 0 { classes = "-" + strings.Join(rna.Classes, ",") } err = p.Save(18*vg.Centimeter, 25*vg.Centimeter, decorate(filepath.Base(rna.Pair[1])+classes, format, rna.Filter), ) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } }