Add possibility to render charts to image.Image
Add interface Imager for ToImage method. Implement ToImage in rasterRenderer. Implement ToImage in Chart, BarChart and PieChart.
This commit is contained in:
parent
102f7a8aa3
commit
b0cccfa7c7
5 changed files with 170 additions and 0 deletions
51
bar_chart.go
51
bar_chart.go
|
@ -2,6 +2,7 @@ package chart
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"image"
|
||||
"io"
|
||||
"math"
|
||||
|
||||
|
@ -132,6 +133,56 @@ func (bc BarChart) Render(rp RendererProvider, w io.Writer) error {
|
|||
return r.Save(w)
|
||||
}
|
||||
|
||||
// ToImage renders the chart with the given renderer to an image.
|
||||
func (bc BarChart) ToImage(r Renderer) (image.Image, error) {
|
||||
imager, ok := r.(Imager)
|
||||
if !ok {
|
||||
return nil, errors.New("Renderer does not support ToImage")
|
||||
}
|
||||
|
||||
if len(bc.Bars) == 0 {
|
||||
return nil, errors.New("Please provide at least one bar.")
|
||||
}
|
||||
|
||||
if bc.Font == nil {
|
||||
defaultFont, err := GetDefaultFont()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bc.defaultFont = defaultFont
|
||||
}
|
||||
r.SetDPI(bc.GetDPI())
|
||||
|
||||
bc.drawBackground(r)
|
||||
|
||||
var canvasBox Box
|
||||
var yt []Tick
|
||||
var yr Range
|
||||
var yf ValueFormatter
|
||||
|
||||
canvasBox = bc.getDefaultCanvasBox()
|
||||
yr = bc.getRanges()
|
||||
yr = bc.setRangeDomains(canvasBox, yr)
|
||||
yf = bc.getValueFormatters()
|
||||
|
||||
if bc.hasAxes() {
|
||||
yt = bc.getAxesTicks(r, yr, yf)
|
||||
canvasBox = bc.getAdjustedCanvasBox(r, canvasBox, yr, yt)
|
||||
yr = bc.setRangeDomains(canvasBox, yr)
|
||||
}
|
||||
|
||||
bc.drawBars(r, canvasBox, yr)
|
||||
bc.drawXAxis(r, canvasBox)
|
||||
bc.drawYAxis(r, canvasBox, yr, yt)
|
||||
|
||||
bc.drawTitle(r)
|
||||
for _, a := range bc.Elements {
|
||||
a(r, canvasBox, bc.styleDefaultsElements())
|
||||
}
|
||||
|
||||
return imager.ToImage(), nil
|
||||
}
|
||||
|
||||
func (bc BarChart) getRanges() Range {
|
||||
var yrange Range
|
||||
if bc.YAxis.Range != nil && !bc.YAxis.Range.IsZero() {
|
||||
|
|
67
chart.go
67
chart.go
|
@ -2,6 +2,7 @@ package chart
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"image"
|
||||
"io"
|
||||
"math"
|
||||
|
||||
|
@ -132,6 +133,72 @@ func (c Chart) Render(rp RendererProvider, w io.Writer) error {
|
|||
return r.Save(w)
|
||||
}
|
||||
|
||||
// ToImage renders the chart with the given renderer to the given io.Writer.
|
||||
func (c Chart) ToImage(r Renderer) (image.Image, error) {
|
||||
imager, ok := r.(Imager)
|
||||
if !ok {
|
||||
return nil, errors.New("Renderer does not support ToImage")
|
||||
}
|
||||
|
||||
if len(c.Series) == 0 {
|
||||
return nil, errors.New("Please provide at least one series")
|
||||
}
|
||||
c.YAxisSecondary.AxisType = YAxisSecondary
|
||||
|
||||
if c.Font == nil {
|
||||
defaultFont, err := GetDefaultFont()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.defaultFont = defaultFont
|
||||
}
|
||||
r.SetDPI(c.GetDPI(DefaultDPI))
|
||||
|
||||
c.drawBackground(r)
|
||||
|
||||
var xt, yt, yta []Tick
|
||||
xr, yr, yra := c.getRanges()
|
||||
canvasBox := c.getDefaultCanvasBox()
|
||||
xf, yf, yfa := c.getValueFormatters()
|
||||
xr, yr, yra = c.setRangeDomains(canvasBox, xr, yr, yra)
|
||||
|
||||
err := c.checkRanges(xr, yr, yra)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c.hasAxes() {
|
||||
xt, yt, yta = c.getAxesTicks(r, xr, yr, yra, xf, yf, yfa)
|
||||
canvasBox = c.getAxisAdjustedCanvasBox(r, canvasBox, xr, yr, yra, xt, yt, yta)
|
||||
xr, yr, yra = c.setRangeDomains(canvasBox, xr, yr, yra)
|
||||
|
||||
// do a second pass in case things haven't settled yet.
|
||||
xt, yt, yta = c.getAxesTicks(r, xr, yr, yra, xf, yf, yfa)
|
||||
canvasBox = c.getAxisAdjustedCanvasBox(r, canvasBox, xr, yr, yra, xt, yt, yta)
|
||||
xr, yr, yra = c.setRangeDomains(canvasBox, xr, yr, yra)
|
||||
}
|
||||
|
||||
if c.hasAnnotationSeries() {
|
||||
canvasBox = c.getAnnotationAdjustedCanvasBox(r, canvasBox, xr, yr, yra, xf, yf, yfa)
|
||||
xr, yr, yra = c.setRangeDomains(canvasBox, xr, yr, yra)
|
||||
xt, yt, yta = c.getAxesTicks(r, xr, yr, yra, xf, yf, yfa)
|
||||
}
|
||||
|
||||
c.drawCanvas(r, canvasBox)
|
||||
c.drawAxes(r, canvasBox, xr, yr, yra, xt, yt, yta)
|
||||
for index, series := range c.Series {
|
||||
c.drawSeries(r, canvasBox, xr, yr, yra, series, index)
|
||||
}
|
||||
|
||||
c.drawTitle(r)
|
||||
|
||||
for _, a := range c.Elements {
|
||||
a(r, canvasBox, c.styleDefaultsElements())
|
||||
}
|
||||
|
||||
return imager.ToImage(), nil
|
||||
}
|
||||
|
||||
func (c Chart) getRanges() (xrange, yrange, yrangeAlt Range) {
|
||||
var minx, maxx float64 = math.MaxFloat64, -math.MaxFloat64
|
||||
var miny, maxy float64 = math.MaxFloat64, -math.MaxFloat64
|
||||
|
|
10
imager.go
Normal file
10
imager.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
package chart
|
||||
|
||||
import "image"
|
||||
|
||||
// Imager interface is implemented by renderers that want to expose the data
|
||||
// as the image.Image interface.
|
||||
type Imager interface {
|
||||
// ToImage returns a image.Image
|
||||
ToImage() image.Image
|
||||
}
|
37
pie_chart.go
37
pie_chart.go
|
@ -2,6 +2,7 @@ package chart
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"image"
|
||||
"io"
|
||||
|
||||
"github.com/golang/freetype/truetype"
|
||||
|
@ -98,6 +99,42 @@ func (pc PieChart) Render(rp RendererProvider, w io.Writer) error {
|
|||
return r.Save(w)
|
||||
}
|
||||
|
||||
// ToImage renders the chart with the given renderer to an image.
|
||||
func (pc PieChart) ToImage(r Renderer) (image.Image, error) {
|
||||
imager, ok := r.(Imager)
|
||||
if !ok {
|
||||
return nil, errors.New("Renderer does not support ToImage")
|
||||
}
|
||||
|
||||
if len(pc.Values) == 0 {
|
||||
return nil, errors.New("Please provide at least one value.")
|
||||
}
|
||||
|
||||
if pc.Font == nil {
|
||||
defaultFont, err := GetDefaultFont()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pc.defaultFont = defaultFont
|
||||
}
|
||||
r.SetDPI(pc.GetDPI(DefaultDPI))
|
||||
|
||||
canvasBox := pc.getDefaultCanvasBox()
|
||||
canvasBox = pc.getCircleAdjustedCanvasBox(canvasBox)
|
||||
|
||||
pc.drawBackground(r)
|
||||
pc.drawCanvas(r, canvasBox)
|
||||
|
||||
finalValues := pc.finalizeValues(pc.Values)
|
||||
pc.drawSlices(r, canvasBox, finalValues)
|
||||
pc.drawTitle(r)
|
||||
for _, a := range pc.Elements {
|
||||
a(r, canvasBox, pc.styleDefaultsElements())
|
||||
}
|
||||
|
||||
return imager.ToImage(), nil
|
||||
}
|
||||
|
||||
func (pc PieChart) drawBackground(r Renderer) {
|
||||
Draw.Box(r, Box{
|
||||
Right: pc.GetWidth(),
|
||||
|
|
|
@ -212,3 +212,8 @@ func (rr *rasterRenderer) ClearTextRotation() {
|
|||
func (rr *rasterRenderer) Save(w io.Writer) error {
|
||||
return png.Encode(w, rr.i)
|
||||
}
|
||||
|
||||
// ToImage implements the Imager interface method.
|
||||
func (rr *rasterRenderer) ToImage() image.Image {
|
||||
return rr.i
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue