go-charts/echarts.go
2021-12-18 09:49:44 +08:00

218 lines
5.3 KiB
Go

// MIT License
// Copyright (c) 2021 Tree Xie
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package charts
import (
"bytes"
"encoding/json"
"regexp"
"strconv"
"strings"
"github.com/wcharczuk/go-chart/v2"
)
type EChartStyle struct {
Color string `json:"color"`
}
type ECharsSeriesData struct {
Value float64 `json:"value"`
Name string `json:"name"`
ItemStyle EChartStyle `json:"itemStyle"`
}
type _ECharsSeriesData ECharsSeriesData
func (es *ECharsSeriesData) UnmarshalJSON(data []byte) error {
data = bytes.TrimSpace(data)
if len(data) == 0 {
return nil
}
if regexp.MustCompile(`^\d+`).Match(data) {
v, err := strconv.ParseFloat(string(data), 64)
if err != nil {
return err
}
es.Value = v
return nil
}
v := _ECharsSeriesData{}
err := json.Unmarshal(data, &v)
if err != nil {
return err
}
es.Name = v.Name
es.Value = v.Value
es.ItemStyle = v.ItemStyle
return nil
}
type EChartsYAxis struct {
Data []struct {
Min int `json:"min"`
Max int `json:"max"`
Interval int `json:"interval"`
AxisLabel struct {
Formatter string `json:"formatter"`
} `json:"axisLabel"`
} `json:"data"`
}
func (ey *EChartsYAxis) UnmarshalJSON(data []byte) error {
data = bytes.TrimSpace(data)
if len(data) == 0 {
return nil
}
if data[0] != '[' {
data = []byte("[" + string(data) + "]")
}
return json.Unmarshal(data, &ey.Data)
}
type ECharsOptions struct {
Theme string `json:"theme"`
Title struct {
Text string `json:"text"`
TextStyle struct {
Color string `json:"color"`
FontFamily string `json:"fontFamily"`
} `json:"textStyle"`
} `json:"title"`
XAxis struct {
Type string `json:"type"`
BoundaryGap *bool `json:"boundaryGap"`
SplitNumber int `json:"splitNumber"`
Data []string `json:"data"`
} `json:"xAxis"`
YAxis EChartsYAxis `json:"yAxis"`
Legend struct {
Data []string `json:"data"`
} `json:"legend"`
Series []struct {
Data []ECharsSeriesData `json:"data"`
Type string `json:"type"`
YAxisIndex int `json:"yAxisIndex"`
} `json:"series"`
}
func convertEChartsSeries(e *ECharsOptions) ([]Series, chart.TickPosition) {
tickPosition := chart.TickPositionUnset
if len(e.Series) == 0 {
return nil, tickPosition
}
seriesType := e.Series[0].Type
if seriesType == SeriesPie {
series := make([]Series, len(e.Series[0].Data))
for index, item := range e.Series[0].Data {
series[index] = Series{
Data: []SeriesData{
{
Value: item.Value,
},
},
Type: seriesType,
Name: item.Name,
}
}
return series, tickPosition
}
series := make([]Series, len(e.Series))
for index, item := range e.Series {
// bar默认tick居中
if item.Type == SeriesBar {
tickPosition = chart.TickPositionBetweenTicks
}
data := make([]SeriesData, len(item.Data))
for j, itemData := range item.Data {
sd := SeriesData{
Value: itemData.Value,
}
if itemData.ItemStyle.Color != "" {
c := parseColor(itemData.ItemStyle.Color)
sd.Style.FillColor = c
sd.Style.StrokeColor = c
}
data[j] = sd
}
series[index] = Series{
YAxisIndex: item.YAxisIndex,
Data: data,
Type: item.Type,
}
}
return series, tickPosition
}
func (e *ECharsOptions) ToOptions() Options {
o := Options{
Theme: e.Theme,
}
o.Title = Title{
Text: e.Title.Text,
}
o.XAxis = XAxis{
Type: e.XAxis.Type,
Data: e.XAxis.Data,
SplitNumber: e.XAxis.SplitNumber,
}
o.Legend = Legend{
Data: e.Legend.Data,
}
if len(e.YAxis.Data) != 0 {
yAxisOptions := make([]*YAxisOption, len(e.YAxis.Data))
for index, item := range e.YAxis.Data {
opt := &YAxisOption{}
template := item.AxisLabel.Formatter
if template != "" {
opt.Formater = func(v interface{}) string {
str := defaultFloatFormater(v)
return strings.ReplaceAll(template, "{value}", str)
}
}
yAxisOptions[index] = opt
}
o.YAxisOptions = yAxisOptions
}
series, tickPosition := convertEChartsSeries(e)
o.Series = series
if e.XAxis.BoundaryGap == nil || *e.XAxis.BoundaryGap {
tickPosition = chart.TickPositionBetweenTicks
}
o.TickPosition = tickPosition
return o
}
func ParseECharsOptions(options string) (Options, error) {
e := ECharsOptions{}
err := json.Unmarshal([]byte(options), &e)
if err != nil {
return Options{}, err
}
return e.ToOptions(), nil
}