package utils
import (
"bytes"
"encoding/base64"
"fmt"
"time"
"gonum.org/v1/plot/plotutil"
"gonum.org/v1/plot"
"gonum.org/v1/plot/plotter"
"gonum.org/v1/plot/vg"
)
type myXTick struct {
}
// Ticks 返回一个包含从min到max的每隔86400*2秒的时间戳,并且对应的标签是格式化为"1/2"的日期字符串的切片。
//
// 参数:
// - min (float64) - 最小时间戳,单位为秒
// - max (float64) - 最大时间戳,单位为秒
//
// 返回值:
// - []plot.Tick ([]plot.Tick) - 包含每个时间戳和对应的标签的切片,类型为plot.Tick,其中Value是时间戳,Label是格式化为"1/2"的日期字符串
func (myXTick *myXTick) Ticks(min, max float64) []plot.Tick {
ticks := []plot.Tick{}
for i := min; i <= max; i += 86400 * 7 {
ticks = append(ticks, plot.Tick{Value: i, Label: time.Unix(int64(i), 0).Format("2006/1/2")})
}
return ticks
}
type myYTick struct {
addSetp float64
}
// Ticks 返回一个包含最小值和最大值之间的所有tick,并且每个tick的label是tick的百分比或者数字。
// 如果最大值小于1,则tick的label为百分比,否则为数字。
//
// 参数:
// - min (float64) - tick的最小值
// - max (float64) - tick的最大值
//
// 返回值:
// - []plot.Tick ([]plot.Tick) - 包含所有tick的切片,每个tick包括value(float64)和label(string)两个属性
func (myYTick *myYTick) Ticks(min, max float64) []plot.Tick {
ticks := []plot.Tick{}
if max < 1 {
for i := min; i <= max; i += 0.05 {
ticks = append(ticks, plot.Tick{Value: i, Label: fmt.Sprintf("%.2f%%", i*100)})
}
} else {
for i := 0.; i <= max+myYTick.addSetp; i += myYTick.addSetp {
v := float64(int(i/100) * 100)
ticks = append(ticks, plot.Tick{Value: v, Label: fmt.Sprintf("%.2f", v)})
}
}
return ticks
}
// GenerateChart2Base64 根据给定的数据,生成一张基于Plot库的折线图,并返回该图片的Base64编码。
// 参数:
// - title string:图表的标题。
// - yList [][]float64:二维切片,每一行代表一条折线,每一列代表一个点的Y值。
// - xList []int32:一维切片,每一个元素代表一个点的X值。
//
// 返回值:
// - string:返回一个Base64编码的字符串,包含了生成的图片。
// - error:如果发生错误,则返回非nil的error。
func GenerateChart2Base64(title string, yList [][]float64, xList []int32, legend []string, lineColors []int, addSetp float64) (string, error) {
if len(yList) != len(legend) {
return "", fmt.Errorf("yList和legend的长度不一致")
}
// 创建一个新的绘图对象
p := plot.New()
//设置标题
p.Title.TextStyle.Font.Size = vg.Length(16) // 标题字体大小
p.Title.Text = title
// 打开网格线
p.Add(plotter.NewGrid())
// 设置绘图的大小
p.X.Scale = plot.LinearScale{}
p.Y.Scale = plot.LinearScale{}
p.Y.Tick.Marker = &myYTick{addSetp: addSetp}
p.Y.Tick.Label.Font.Size = vg.Length(12) // X轴的标签字体大小
p.Y.Tick.Width = vg.Length(2) // X轴的网格线宽度
//p.X.Tick.Marker = plot.TimeTicks{Format: "20060102"}
p.X.Tick.Marker = &myXTick{}
p.X.Tick.Label.Font.Size = vg.Length(12) // Y轴的标签字体大小
p.X.Tick.Width = vg.Length(2) // Y轴的网格线宽度
var maxY float64
if lineColors == nil {
lineColors = []int{8, 14, 16}
}
for i := 0; i < len(yList); i++ {
xys := plotter.XYs{}
for j := 0; j < len(yList[i]); j++ {
xys = append(xys, plotter.XY{
X: float64(xList[j]),
Y: yList[i][j],
})
if yList[i][j] > maxY {
maxY = yList[i][j]
}
}
// 创建折线图
line, err := plotter.NewLine(xys)
if err != nil {
return "", err
}
line.LineStyle.Width = vg.Points(4)
line.LineStyle.Color = plotutil.Color(lineColors[i])
p.Add(line)
p.Legend.Add(legend[i], line)
}
p.Legend.TextStyle.Font.Size = vg.Length(12) // 图例字体大小
p.Legend.Top = true // 图例在上方显示
p.Legend.Left = true
// 创建折线图
line, err := plotter.NewLine(plotter.XYs{{
X: float64(xList[0]),
Y: 0,
}, {
X: float64(xList[0]),
Y: maxY + addSetp,
}})
if err != nil {
return "", err
}
line.LineStyle.Width = vg.Points(0)
p.Add(line)
// 将图表渲染为PNG格式的图片
var buf bytes.Buffer
// p.Save(1200*vg.Millimeter, 400*vg.Millimeter, "./123.png")
i, err := p.WriterTo(317*vg.Millimeter, 100*vg.Millimeter, "png")
if err != nil {
return "", err
}
_, err = i.WriteTo(&buf)
if err != nil {
return "", err
}
// 将图片转换为Base64字符串
encoded := base64.StdEncoding.EncodeToString(buf.Bytes())
// log.Println(encoded)
return encoded, nil
}
go使用plot包实现自定义的趋势图代码,而且可以实现自定义的坐标轴
最新推荐文章于 2025-02-05 08:48:16 发布