feat: support radar option of echarts
This commit is contained in:
parent
570828d35f
commit
b93d096633
10 changed files with 411 additions and 56 deletions
|
|
@ -152,6 +152,11 @@ The name with `[]` is new parameter, others are the same as `echarts`.
|
||||||
- `legend.padding` legend space around content
|
- `legend.padding` legend space around content
|
||||||
- `legend.left` Distance between legend component and the left side of the container. Left value can be instant pixel value like 20; it can also be a percentage value relative to container width like '20%'; and it can also be 'left', 'center', or 'right'.
|
- `legend.left` Distance between legend component and the left side of the container. Left value can be instant pixel value like 20; it can also be a percentage value relative to container width like '20%'; and it can also be 'left', 'center', or 'right'.
|
||||||
- `legend.top` Distance between legend component and the top side of the container. Top value can be instant pixel value like 20
|
- `legend.top` Distance between legend component and the top side of the container. Top value can be instant pixel value like 20
|
||||||
|
- `radar` Coordinate for radar charts
|
||||||
|
- `radar.indicator` Indicator of radar chart, which is used to assign multiple variables(dimensions) in radar chart
|
||||||
|
- `radar.indicator.name` Indicator's name
|
||||||
|
- `radar.indicator.max` The maximum value of indicator
|
||||||
|
- `radar.indicator.min` The minimum value of indicator, default value is 0.
|
||||||
- `series` The series for chart
|
- `series` The series for chart
|
||||||
- `series.name` Series name used for displaying in legend.
|
- `series.name` Series name used for displaying in legend.
|
||||||
- `series.type` Series type: `line`, `bar` or`pie`
|
- `series.type` Series type: `line`, `bar` or`pie`
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
19
draw.go
19
draw.go
|
|
@ -322,3 +322,22 @@ func (d *Draw) polygon(center Point, radius float64, sides int) {
|
||||||
d.lineTo(points[0].X, points[0].Y)
|
d.lineTo(points[0].X, points[0].Y)
|
||||||
d.Render.Stroke()
|
d.Render.Stroke()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Draw) fill(points []Point, s chart.Style) {
|
||||||
|
if !s.ShouldDrawFill() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r := d.Render
|
||||||
|
var x, y int
|
||||||
|
s.GetFillOptions().WriteDrawingOptionsToRenderer(r)
|
||||||
|
for index, point := range points {
|
||||||
|
x = point.X
|
||||||
|
y = point.Y
|
||||||
|
if index == 0 {
|
||||||
|
d.moveTo(x, y)
|
||||||
|
} else {
|
||||||
|
d.lineTo(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.Fill()
|
||||||
|
}
|
||||||
|
|
|
||||||
50
draw_test.go
50
draw_test.go
|
|
@ -409,6 +409,56 @@ func TestDraw(t *testing.T) {
|
||||||
},
|
},
|
||||||
result: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"400\" height=\"300\">\\n<circle cx=\"5\" cy=\"30\" r=\"3\" style=\"stroke-width:1;stroke:rgba(84,112,198,1.0);fill:rgba(84,112,198,1.0)\"/><path stroke-dasharray=\"4.0, 2.0\" d=\"\" style=\"stroke-width:1;stroke:rgba(84,112,198,1.0);fill:rgba(84,112,198,1.0)\"/><path stroke-dasharray=\"4.0, 2.0\" d=\"M 10 30\nL 289 30\" style=\"stroke-width:1;stroke:rgba(84,112,198,1.0);fill:rgba(84,112,198,1.0)\"/><path d=\"M 289 25\nL 305 30\nL 289 35\nL 294 30\nL 289 25\" style=\"stroke-width:1;stroke:rgba(84,112,198,1.0);fill:rgba(84,112,198,1.0)\"/></svg>",
|
result: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"400\" height=\"300\">\\n<circle cx=\"5\" cy=\"30\" r=\"3\" style=\"stroke-width:1;stroke:rgba(84,112,198,1.0);fill:rgba(84,112,198,1.0)\"/><path stroke-dasharray=\"4.0, 2.0\" d=\"\" style=\"stroke-width:1;stroke:rgba(84,112,198,1.0);fill:rgba(84,112,198,1.0)\"/><path stroke-dasharray=\"4.0, 2.0\" d=\"M 10 30\nL 289 30\" style=\"stroke-width:1;stroke:rgba(84,112,198,1.0);fill:rgba(84,112,198,1.0)\"/><path d=\"M 289 25\nL 305 30\nL 289 35\nL 294 30\nL 289 25\" style=\"stroke-width:1;stroke:rgba(84,112,198,1.0);fill:rgba(84,112,198,1.0)\"/></svg>",
|
||||||
},
|
},
|
||||||
|
// polygon
|
||||||
|
{
|
||||||
|
fn: func(d *Draw) {
|
||||||
|
chart.Style{
|
||||||
|
StrokeWidth: 1,
|
||||||
|
StrokeColor: drawing.Color{
|
||||||
|
R: 84,
|
||||||
|
G: 112,
|
||||||
|
B: 198,
|
||||||
|
A: 255,
|
||||||
|
},
|
||||||
|
}.WriteToRenderer(d.Render)
|
||||||
|
d.polygon(Point{
|
||||||
|
X: 100,
|
||||||
|
Y: 100,
|
||||||
|
}, 50, 6)
|
||||||
|
},
|
||||||
|
result: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"400\" height=\"300\">\\n<path d=\"M 105 60\nL 148 85\nL 148 134\nL 105 160\nL 62 135\nL 62 86\nL 105 60\" style=\"stroke-width:1;stroke:rgba(84,112,198,1.0);fill:none\"/></svg>",
|
||||||
|
},
|
||||||
|
// fill
|
||||||
|
{
|
||||||
|
fn: func(d *Draw) {
|
||||||
|
d.fill([]Point{
|
||||||
|
{
|
||||||
|
X: 0,
|
||||||
|
Y: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
X: 0,
|
||||||
|
Y: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
X: 100,
|
||||||
|
Y: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
X: 0,
|
||||||
|
Y: 0,
|
||||||
|
},
|
||||||
|
}, chart.Style{
|
||||||
|
FillColor: drawing.Color{
|
||||||
|
R: 84,
|
||||||
|
G: 112,
|
||||||
|
B: 198,
|
||||||
|
A: 255,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
result: "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"400\" height=\"300\">\\n<path d=\"M 5 10\nL 5 110\nL 105 110\nL 5 10\" style=\"stroke-width:0;stroke:none;fill:rgba(84,112,198,1.0)\"/></svg>",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
d, err := NewDraw(DrawOption{
|
d, err := NewDraw(DrawOption{
|
||||||
|
|
|
||||||
69
echarts.go
69
echarts.go
|
|
@ -69,10 +69,30 @@ func (es *EChartStyle) ToStyle() chart.Style {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EChartsSeriesDataValue struct {
|
||||||
|
values []float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (value *EChartsSeriesDataValue) UnmarshalJSON(data []byte) error {
|
||||||
|
data = convertToArray(data)
|
||||||
|
return json.Unmarshal(data, &value.values)
|
||||||
|
}
|
||||||
|
func (value *EChartsSeriesDataValue) First() float64 {
|
||||||
|
if len(value.values) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return value.values[0]
|
||||||
|
}
|
||||||
|
func NewEChartsSeriesDataValue(values ...float64) EChartsSeriesDataValue {
|
||||||
|
return EChartsSeriesDataValue{
|
||||||
|
values: values,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type EChartsSeriesData struct {
|
type EChartsSeriesData struct {
|
||||||
Value float64 `json:"value"`
|
Value EChartsSeriesDataValue `json:"value"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
ItemStyle EChartStyle `json:"itemStyle"`
|
ItemStyle EChartStyle `json:"itemStyle"`
|
||||||
}
|
}
|
||||||
type _EChartsSeriesData EChartsSeriesData
|
type _EChartsSeriesData EChartsSeriesData
|
||||||
|
|
||||||
|
|
@ -88,7 +108,11 @@ func (es *EChartsSeriesData) UnmarshalJSON(data []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
es.Value = v
|
es.Value = EChartsSeriesDataValue{
|
||||||
|
values: []float64{
|
||||||
|
v,
|
||||||
|
},
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
v := _EChartsSeriesData{}
|
v := _EChartsSeriesData{}
|
||||||
|
|
@ -291,7 +315,7 @@ func (esList EChartsSeriesList) ToSeriesList() SeriesList {
|
||||||
if item.Type == ChartTypePie {
|
if item.Type == ChartTypePie {
|
||||||
for _, dataItem := range item.Data {
|
for _, dataItem := range item.Data {
|
||||||
seriesList = append(seriesList, Series{
|
seriesList = append(seriesList, Series{
|
||||||
Type: ChartTypePie,
|
Type: item.Type,
|
||||||
Name: dataItem.Name,
|
Name: dataItem.Name,
|
||||||
Label: SeriesLabel{
|
Label: SeriesLabel{
|
||||||
Show: true,
|
Show: true,
|
||||||
|
|
@ -299,17 +323,28 @@ func (esList EChartsSeriesList) ToSeriesList() SeriesList {
|
||||||
Radius: item.Radius,
|
Radius: item.Radius,
|
||||||
Data: []SeriesData{
|
Data: []SeriesData{
|
||||||
{
|
{
|
||||||
Value: dataItem.Value,
|
Value: dataItem.Value.First(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// 如果是radar
|
||||||
|
if item.Type == ChartTypeRadar {
|
||||||
|
for _, dataItem := range item.Data {
|
||||||
|
seriesList = append(seriesList, Series{
|
||||||
|
Name: dataItem.Name,
|
||||||
|
Type: item.Type,
|
||||||
|
Data: NewSeriesDataFromValues(dataItem.Value.values),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
data := make([]SeriesData, len(item.Data))
|
data := make([]SeriesData, len(item.Data))
|
||||||
for j, dataItem := range item.Data {
|
for j, dataItem := range item.Data {
|
||||||
data[j] = SeriesData{
|
data[j] = SeriesData{
|
||||||
Value: dataItem.Value,
|
Value: dataItem.Value.First(),
|
||||||
Style: dataItem.ItemStyle.ToStyle(),
|
Style: dataItem.ItemStyle.ToStyle(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -364,9 +399,12 @@ type EChartsOption struct {
|
||||||
TextStyle EChartsTextStyle `json:"textStyle"`
|
TextStyle EChartsTextStyle `json:"textStyle"`
|
||||||
SubtextStyle EChartsTextStyle `json:"subtextStyle"`
|
SubtextStyle EChartsTextStyle `json:"subtextStyle"`
|
||||||
} `json:"title"`
|
} `json:"title"`
|
||||||
XAxis EChartsXAxis `json:"xAxis"`
|
XAxis EChartsXAxis `json:"xAxis"`
|
||||||
YAxis EChartsYAxis `json:"yAxis"`
|
YAxis EChartsYAxis `json:"yAxis"`
|
||||||
Legend EChartsLegend `json:"legend"`
|
Legend EChartsLegend `json:"legend"`
|
||||||
|
Radar struct {
|
||||||
|
Indicator []RadarIndicator `json:"indicator"`
|
||||||
|
} `json:"radar"`
|
||||||
Series EChartsSeriesList `json:"series"`
|
Series EChartsSeriesList `json:"series"`
|
||||||
Children []EChartsOption `json:"children"`
|
Children []EChartsOption `json:"children"`
|
||||||
}
|
}
|
||||||
|
|
@ -397,11 +435,12 @@ func (eo *EChartsOption) ToOption() ChartOption {
|
||||||
Align: eo.Legend.Align,
|
Align: eo.Legend.Align,
|
||||||
Orient: eo.Legend.Orient,
|
Orient: eo.Legend.Orient,
|
||||||
},
|
},
|
||||||
Width: eo.Width,
|
RadarIndicators: eo.Radar.Indicator,
|
||||||
Height: eo.Height,
|
Width: eo.Width,
|
||||||
Padding: eo.Padding.Box,
|
Height: eo.Height,
|
||||||
Box: eo.Box,
|
Padding: eo.Padding.Box,
|
||||||
SeriesList: eo.Series.ToSeriesList(),
|
Box: eo.Box,
|
||||||
|
SeriesList: eo.Series.ToSeriesList(),
|
||||||
}
|
}
|
||||||
if len(eo.XAxis.Data) != 0 {
|
if len(eo.XAxis.Data) != 0 {
|
||||||
xAxisData := eo.XAxis.Data[0]
|
xAxisData := eo.XAxis.Data[0]
|
||||||
|
|
|
||||||
|
|
@ -253,14 +253,14 @@ func TestEChartsSeriesData(t *testing.T) {
|
||||||
err := esd.UnmarshalJSON([]byte(`123`))
|
err := esd.UnmarshalJSON([]byte(`123`))
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
assert.Equal(EChartsSeriesData{
|
assert.Equal(EChartsSeriesData{
|
||||||
Value: 123,
|
Value: NewEChartsSeriesDataValue(123),
|
||||||
}, esd)
|
}, esd)
|
||||||
|
|
||||||
esd = EChartsSeriesData{}
|
esd = EChartsSeriesData{}
|
||||||
err = esd.UnmarshalJSON([]byte(`2.1`))
|
err = esd.UnmarshalJSON([]byte(`2.1`))
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
assert.Equal(EChartsSeriesData{
|
assert.Equal(EChartsSeriesData{
|
||||||
Value: 2.1,
|
Value: NewEChartsSeriesDataValue(2.1),
|
||||||
}, esd)
|
}, esd)
|
||||||
|
|
||||||
esd = EChartsSeriesData{}
|
esd = EChartsSeriesData{}
|
||||||
|
|
@ -273,7 +273,7 @@ func TestEChartsSeriesData(t *testing.T) {
|
||||||
}`))
|
}`))
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
assert.Equal(EChartsSeriesData{
|
assert.Equal(EChartsSeriesData{
|
||||||
Value: 123.12,
|
Value: NewEChartsSeriesDataValue(123.12),
|
||||||
Name: "test",
|
Name: "test",
|
||||||
ItemStyle: EChartStyle{
|
ItemStyle: EChartStyle{
|
||||||
Color: "#aaa",
|
Color: "#aaa",
|
||||||
|
|
@ -308,10 +308,10 @@ func TestEChartsSeries(t *testing.T) {
|
||||||
Name: "Email",
|
Name: "Email",
|
||||||
Data: []EChartsSeriesData{
|
Data: []EChartsSeriesData{
|
||||||
{
|
{
|
||||||
Value: 120,
|
Value: NewEChartsSeriesDataValue(120),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Value: 132,
|
Value: NewEChartsSeriesDataValue(132),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -320,10 +320,10 @@ func TestEChartsSeries(t *testing.T) {
|
||||||
Type: "bar",
|
Type: "bar",
|
||||||
Data: []EChartsSeriesData{
|
Data: []EChartsSeriesData{
|
||||||
{
|
{
|
||||||
Value: 220,
|
Value: NewEChartsSeriesDataValue(220),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Value: 182,
|
Value: NewEChartsSeriesDataValue(182),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -430,12 +430,20 @@ func TestEChartsSeriesList(t *testing.T) {
|
||||||
Radius: "30%",
|
Radius: "30%",
|
||||||
Data: []EChartsSeriesData{
|
Data: []EChartsSeriesData{
|
||||||
{
|
{
|
||||||
Name: "1",
|
Name: "1",
|
||||||
Value: 1,
|
Value: EChartsSeriesDataValue{
|
||||||
|
values: []float64{
|
||||||
|
1,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "2",
|
Name: "2",
|
||||||
Value: 2,
|
Value: EChartsSeriesDataValue{
|
||||||
|
values: []float64{
|
||||||
|
2,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -474,13 +482,13 @@ func TestEChartsSeriesList(t *testing.T) {
|
||||||
Type: ChartTypeBar,
|
Type: ChartTypeBar,
|
||||||
Data: []EChartsSeriesData{
|
Data: []EChartsSeriesData{
|
||||||
{
|
{
|
||||||
Value: 1,
|
Value: NewEChartsSeriesDataValue(1),
|
||||||
ItemStyle: EChartStyle{
|
ItemStyle: EChartStyle{
|
||||||
Color: "#aaa",
|
Color: "#aaa",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Value: 2,
|
Value: NewEChartsSeriesDataValue(2),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
YAxisIndex: 1,
|
YAxisIndex: 1,
|
||||||
|
|
@ -488,10 +496,10 @@ func TestEChartsSeriesList(t *testing.T) {
|
||||||
{
|
{
|
||||||
Data: []EChartsSeriesData{
|
Data: []EChartsSeriesData{
|
||||||
{
|
{
|
||||||
Value: 3,
|
Value: NewEChartsSeriesDataValue(3),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Value: 4,
|
Value: NewEChartsSeriesDataValue(4),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ItemStyle: EChartStyle{
|
ItemStyle: EChartStyle{
|
||||||
|
|
|
||||||
|
|
@ -1483,6 +1483,75 @@ func echartsHandler(w http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}`,
|
}`,
|
||||||
|
`{
|
||||||
|
"title": {
|
||||||
|
"text": "Basic Radar Chart"
|
||||||
|
},
|
||||||
|
"legend": {
|
||||||
|
"data": [
|
||||||
|
"Allocated Budget",
|
||||||
|
"Actual Spending"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"radar": {
|
||||||
|
"indicator": [
|
||||||
|
{
|
||||||
|
"name": "Sales",
|
||||||
|
"max": 6500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Administration",
|
||||||
|
"max": 16000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Information Technology",
|
||||||
|
"max": 30000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Customer Support",
|
||||||
|
"max": 38000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Development",
|
||||||
|
"max": 52000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Marketing",
|
||||||
|
"max": 25000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"series": [
|
||||||
|
{
|
||||||
|
"name": "Budget vs spending",
|
||||||
|
"type": "radar",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"value": [
|
||||||
|
4200,
|
||||||
|
3000,
|
||||||
|
20000,
|
||||||
|
35000,
|
||||||
|
50000,
|
||||||
|
18000
|
||||||
|
],
|
||||||
|
"name": "Allocated Budget"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": [
|
||||||
|
5000,
|
||||||
|
14000,
|
||||||
|
28000,
|
||||||
|
26000,
|
||||||
|
42000,
|
||||||
|
21000
|
||||||
|
],
|
||||||
|
"name": "Actual Spending"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`,
|
||||||
`{
|
`{
|
||||||
"legend": {
|
"legend": {
|
||||||
"top": "-140",
|
"top": "-140",
|
||||||
|
|
|
||||||
32
line.go
32
line.go
|
|
@ -55,25 +55,23 @@ func (d *Draw) lineFill(points []Point, style LineStyle) {
|
||||||
if !(s.ShouldDrawStroke() && s.ShouldDrawFill()) {
|
if !(s.ShouldDrawStroke() && s.ShouldDrawFill()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
r := d.Render
|
|
||||||
var x, y int
|
newPoints := make([]Point, len(points))
|
||||||
s.GetFillOptions().WriteDrawingOptionsToRenderer(r)
|
copy(newPoints, points)
|
||||||
for index, point := range points {
|
|
||||||
x = point.X
|
|
||||||
y = point.Y
|
|
||||||
if index == 0 {
|
|
||||||
d.moveTo(x, y)
|
|
||||||
} else {
|
|
||||||
d.lineTo(x, y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
height := d.Box.Height()
|
|
||||||
d.lineTo(x, height)
|
|
||||||
x0 := points[0].X
|
x0 := points[0].X
|
||||||
y0 := points[0].Y
|
y0 := points[0].Y
|
||||||
d.lineTo(x0, height)
|
height := d.Box.Height()
|
||||||
d.lineTo(x0, y0)
|
newPoints = append(newPoints, Point{
|
||||||
r.Fill()
|
X: points[len(points)-1].X,
|
||||||
|
Y: height,
|
||||||
|
}, Point{
|
||||||
|
X: x0,
|
||||||
|
Y: height,
|
||||||
|
}, Point{
|
||||||
|
X: x0,
|
||||||
|
Y: y0,
|
||||||
|
})
|
||||||
|
d.fill(newPoints, style.Style())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Draw) lineDot(points []Point, style LineStyle) {
|
func (d *Draw) lineDot(points []Point, style LineStyle) {
|
||||||
|
|
|
||||||
|
|
@ -30,12 +30,13 @@ import (
|
||||||
"github.com/wcharczuk/go-chart/v2/drawing"
|
"github.com/wcharczuk/go-chart/v2/drawing"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 线 E0E6F1
|
|
||||||
// 填充 rgb(210,219,238) fill-opacity="0.2"
|
|
||||||
|
|
||||||
type RadarIndicator struct {
|
type RadarIndicator struct {
|
||||||
|
// Indicator's name
|
||||||
Name string
|
Name string
|
||||||
Max float64
|
// The maximum value of indicator
|
||||||
|
Max float64
|
||||||
|
// The minimum value of indicator
|
||||||
|
Min float64
|
||||||
}
|
}
|
||||||
|
|
||||||
type radarChartOption struct {
|
type radarChartOption struct {
|
||||||
|
|
@ -150,18 +151,19 @@ func radarChartRender(opt radarChartOption, result *basicRenderResult) error {
|
||||||
// 雷达图
|
// 雷达图
|
||||||
angles := getPolygonPointAngles(sides)
|
angles := getPolygonPointAngles(sides)
|
||||||
maxCount := len(opt.Indicators)
|
maxCount := len(opt.Indicators)
|
||||||
for i, series := range opt.SeriesList {
|
for _, series := range opt.SeriesList {
|
||||||
linePoints := make([]Point, 0, maxCount)
|
linePoints := make([]Point, 0, maxCount)
|
||||||
for j, item := range series.Data {
|
for j, item := range series.Data {
|
||||||
if j >= maxCount {
|
if j >= maxCount {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
percent := item.Value / opt.Indicators[j].Max
|
indicator := opt.Indicators[j]
|
||||||
|
percent := (item.Value - indicator.Min) / (indicator.Max - indicator.Min)
|
||||||
r := percent * radius
|
r := percent * radius
|
||||||
p := getPolygonPoint(center, r, angles[j])
|
p := getPolygonPoint(center, r, angles[j])
|
||||||
linePoints = append(linePoints, p)
|
linePoints = append(linePoints, p)
|
||||||
}
|
}
|
||||||
color := theme.GetSeriesColor(i)
|
color := theme.GetSeriesColor(series.index)
|
||||||
dotFillColor := drawing.ColorWhite
|
dotFillColor := drawing.ColorWhite
|
||||||
if theme.IsDark() {
|
if theme.IsDark() {
|
||||||
dotFillColor = color
|
dotFillColor = color
|
||||||
|
|
@ -176,7 +178,7 @@ func radarChartRender(opt radarChartOption, result *basicRenderResult) error {
|
||||||
FillColor: color.WithAlpha(20),
|
FillColor: color.WithAlpha(20),
|
||||||
}
|
}
|
||||||
d.lineStroke(linePoints, s)
|
d.lineStroke(linePoints, s)
|
||||||
d.lineFill(linePoints, s)
|
d.fill(linePoints, s.Style())
|
||||||
d.lineDot(linePoints[0:len(linePoints)-1], s)
|
d.lineDot(linePoints[0:len(linePoints)-1], s)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
102
radar_chart_test.go
Normal file
102
radar_chart_test.go
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue