This closes #1474, support set the format for the data series fill (solid fill)

- Breaking changes: remove the `Color` field in the `ChartLine` structure
- This support set the bubble size in a data series
- Unit test update and correct the docs of the function `GetSheetDimension`
This commit is contained in:
xuri 2023-02-17 20:03:46 +08:00
parent ad90cea78b
commit c2d6707a85
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
5 changed files with 64 additions and 27 deletions

View File

@ -657,7 +657,9 @@ func parseChartOptions(opts *Chart) (*Chart, error) {
//
// Name
// Categories
// Sizes
// Values
// Fill
// Line
// Marker
//
@ -670,16 +672,18 @@ func parseChartOptions(opts *Chart) (*Chart, error) {
// the same as the X axis. In most chart types the 'Categories' property is
// optional and the chart will just assume a sequential series from 1..n.
//
// Sizes: This sets the bubble size in a data series.
//
// Values: This is the most important property of a series and is the only
// mandatory option for every chart object. This option links the chart with
// the worksheet data that it displays.
//
// Fill: This set the format for the data series fill.
//
// Line: This sets the line format of the line chart. The 'Line' property is
// optional and if it isn't supplied it will default style. The options that
// can be set are width and color. The range of width is 0.25pt - 999pt. If the
// value of width is outside the range, the default width of the line is 2pt.
// The value for color should be represented in hex format
// (e.g., #000000 - #FFFFFF)
//
// Marker: This sets the marker of the line chart and scatter chart. The range
// of optional field 'Size' is 2-72 (default value is 5). The enumeration value

View File

@ -153,7 +153,9 @@ func TestAddChart(t *testing.T) {
{Name: "Sheet1!$A$37", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$37:$D$37"},
}
series2 := []ChartSeries{
{Name: "Sheet1!$A$30", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$30:$D$30", Marker: ChartMarker{Symbol: "none", Size: 10}, Line: ChartLine{Color: "#000000"}},
{Name: "Sheet1!$A$30", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$30:$D$30",
Fill: Fill{Type: "pattern", Color: []string{"000000"}, Pattern: 1},
Marker: ChartMarker{Symbol: "none", Size: 10}},
{Name: "Sheet1!$A$31", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$31:$D$31"},
{Name: "Sheet1!$A$32", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$32:$D$32"},
{Name: "Sheet1!$A$33", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$33:$D$33"},
@ -163,6 +165,16 @@ func TestAddChart(t *testing.T) {
{Name: "Sheet1!$A$37", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$37:$D$37", Line: ChartLine{Width: 0.25}},
}
series3 := []ChartSeries{{Name: "Sheet1!$A$30", Categories: "Sheet1!$A$30:$D$37", Values: "Sheet1!$B$30:$B$37"}}
series4 := []ChartSeries{
{Name: "Sheet1!$A$30", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$30:$D$30", Sizes: "Sheet1!$B$30:$D$30"},
{Name: "Sheet1!$A$31", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$31:$D$31", Sizes: "Sheet1!$B$31:$D$31"},
{Name: "Sheet1!$A$32", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$32:$D$32", Sizes: "Sheet1!$B$32:$D$32"},
{Name: "Sheet1!$A$33", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$33:$D$33", Sizes: "Sheet1!$B$33:$D$33"},
{Name: "Sheet1!$A$34", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$34:$D$34", Sizes: "Sheet1!$B$34:$D$34"},
{Name: "Sheet1!$A$35", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$35:$D$35", Sizes: "Sheet1!$B$35:$D$35"},
{Name: "Sheet1!$A$36", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$36:$D$36", Sizes: "Sheet1!$B$36:$D$36"},
{Name: "Sheet1!$A$37", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$37:$D$37", Sizes: "Sheet1!$B$37:$D$37"},
}
format := GraphicOptions{
ScaleX: defaultPictureScale,
ScaleY: defaultPictureScale,
@ -242,8 +254,8 @@ func TestAddChart(t *testing.T) {
{sheetName: "Sheet2", cell: "AV32", opts: &Chart{Type: "contour", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "Contour Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "BD1", opts: &Chart{Type: "wireframeContour", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "Wireframe Contour Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
// bubble chart
{sheetName: "Sheet2", cell: "BD16", opts: &Chart{Type: "bubble", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "Bubble Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "BD32", opts: &Chart{Type: "bubble3D", Series: series, Format: format, Legend: legend, Title: ChartTitle{Name: "Bubble 3D Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}},
{sheetName: "Sheet2", cell: "BD16", opts: &Chart{Type: "bubble", Series: series4, Format: format, Legend: legend, Title: ChartTitle{Name: "Bubble Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
{sheetName: "Sheet2", cell: "BD32", opts: &Chart{Type: "bubble3D", Series: series4, Format: format, Legend: legend, Title: ChartTitle{Name: "Bubble 3D Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}},
// pie of pie chart
{sheetName: "Sheet2", cell: "BD48", opts: &Chart{Type: "pieOfPie", Series: series3, Format: format, Legend: legend, Title: ChartTitle{Name: "Pie of Pie Chart"}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}},
// bar of pie chart
@ -256,18 +268,14 @@ func TestAddChart(t *testing.T) {
assert.NoError(t, err)
clusteredColumnCombo := [][]string{
{"A1", "line", "Clustered Column - Line Chart"},
{"I1", "bubble", "Clustered Column - Bubble Chart"},
{"Q1", "bubble3D", "Clustered Column - Bubble 3D Chart"},
{"Y1", "doughnut", "Clustered Column - Doughnut Chart"},
{"I1", "doughnut", "Clustered Column - Doughnut Chart"},
}
for _, props := range clusteredColumnCombo {
assert.NoError(t, f.AddChart("Combo Charts", props[0], &Chart{Type: "col", Series: series[:4], Format: format, Legend: legend, Title: ChartTitle{Name: props[2]}, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}}, &Chart{Type: props[1], Series: series[4:], Format: format, Legend: legend, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}}))
}
stackedAreaCombo := map[string][]string{
"A16": {"line", "Stacked Area - Line Chart"},
"I16": {"bubble", "Stacked Area - Bubble Chart"},
"Q16": {"bubble3D", "Stacked Area - Bubble 3D Chart"},
"Y16": {"doughnut", "Stacked Area - Doughnut Chart"},
"I16": {"doughnut", "Stacked Area - Doughnut Chart"},
}
for axis, props := range stackedAreaCombo {
assert.NoError(t, f.AddChart("Combo Charts", axis, &Chart{Type: "areaStacked", Series: series[:4], Format: format, Legend: legend, Title: ChartTitle{Name: props[1]}, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}}, &Chart{Type: props[0], Series: series[4:], Format: format, Legend: legend, PlotArea: ChartPlotArea{ShowBubbleSize: true, ShowCatName: false, ShowLeaderLines: false, ShowPercent: true, ShowSerName: true, ShowVal: true}}))

View File

@ -234,8 +234,8 @@ func (f *File) addChart(opts *Chart, comboCharts []*Chart) {
WireframeSurface3D: f.drawSurface3DChart,
Contour: f.drawSurfaceChart,
WireframeContour: f.drawSurfaceChart,
Bubble: f.drawBaseChart,
Bubble3D: f.drawBaseChart,
Bubble: f.drawBubbleChart,
Bubble3D: f.drawBubbleChart,
}
if opts.Legend.Position == "none" {
xlsxChartSpace.Chart.Legend = nil
@ -270,7 +270,7 @@ func (f *File) drawBaseChart(opts *Chart) *cPlotArea {
Val: stringPtr("col"),
},
Grouping: &attrValString{
Val: stringPtr("clustered"),
Val: stringPtr(plotAreaChartGrouping[opts.Type]),
},
VaryColors: &attrValBool{
Val: opts.VaryColors,
@ -288,9 +288,6 @@ func (f *File) drawBaseChart(opts *Chart) *cPlotArea {
if *c.BarDir.Val, ok = plotAreaChartBarDir[opts.Type]; !ok {
c.BarDir = nil
}
if *c.Grouping.Val, ok = plotAreaChartGrouping[opts.Type]; !ok {
c.Grouping = nil
}
if *c.Overlap.Val, ok = plotAreaChartOverlap[opts.Type]; !ok {
c.Overlap = nil
}
@ -726,6 +723,26 @@ func (f *File) drawSurfaceChart(opts *Chart) *cPlotArea {
return plotArea
}
// drawBubbleChart provides a function to draw the c:bubbleChart element by
// given format sets.
func (f *File) drawBubbleChart(opts *Chart) *cPlotArea {
plotArea := &cPlotArea{
BubbleChart: &cCharts{
VaryColors: &attrValBool{
Val: opts.VaryColors,
},
Ser: f.drawChartSeries(opts),
DLbls: f.drawChartDLbls(opts),
AxID: []*attrValInt{
{Val: intPtr(754001152)},
{Val: intPtr(753999904)},
},
},
ValAx: []*cAxs{f.drawPlotAreaCatAx(opts)[0], f.drawPlotAreaValAx(opts)[0]},
}
return plotArea
}
// drawChartShape provides a function to draw the c:shape element by given
// format sets.
func (f *File) drawChartShape(opts *Chart) *attrValString {
@ -794,13 +811,13 @@ func (f *File) drawChartSeriesSpPr(i int, opts *Chart) *cSpPr {
var srgbClr *attrValString
var schemeClr *aSchemeClr
if color := stringPtr(opts.Series[i].Line.Color); *color != "" {
*color = strings.TrimPrefix(*color, "#")
srgbClr = &attrValString{Val: color}
if color := opts.Series[i].Fill.Color; len(color) == 1 {
srgbClr = &attrValString{Val: stringPtr(strings.TrimPrefix(color[0], "#"))}
} else {
schemeClr = &aSchemeClr{Val: "accent" + strconv.Itoa((opts.order+i)%6+1)}
}
spPr := &cSpPr{SolidFill: &aSolidFill{SchemeClr: schemeClr, SrgbClr: srgbClr}}
spPrScatter := &cSpPr{
Ln: &aLn{
W: 25400,
@ -817,8 +834,15 @@ func (f *File) drawChartSeriesSpPr(i int, opts *Chart) *cSpPr {
},
},
}
chartSeriesSpPr := map[string]*cSpPr{Line: spPrLine, Scatter: spPrScatter}
return chartSeriesSpPr[opts.Type]
if chartSeriesSpPr, ok := map[string]*cSpPr{
Line: spPrLine, Scatter: spPrScatter,
}[opts.Type]; ok {
return chartSeriesSpPr
}
if srgbClr != nil {
return spPr
}
return nil
}
// drawChartSeriesDPt provides a function to draw the c:dPt element by given
@ -923,7 +947,7 @@ func (f *File) drawChartSeriesXVal(v ChartSeries, opts *Chart) *cCat {
F: v.Categories,
},
}
chartSeriesXVal := map[string]*cCat{Scatter: cat}
chartSeriesXVal := map[string]*cCat{Scatter: cat, Bubble: cat, Bubble3D: cat}
return chartSeriesXVal[opts.Type]
}
@ -942,12 +966,12 @@ func (f *File) drawChartSeriesYVal(v ChartSeries, opts *Chart) *cVal {
// drawCharSeriesBubbleSize provides a function to draw the c:bubbleSize
// element by given chart series and format sets.
func (f *File) drawCharSeriesBubbleSize(v ChartSeries, opts *Chart) *cVal {
if _, ok := map[string]bool{Bubble: true, Bubble3D: true}[opts.Type]; !ok {
if _, ok := map[string]bool{Bubble: true, Bubble3D: true}[opts.Type]; !ok || v.Sizes == "" {
return nil
}
return &cVal{
NumRef: &cNumRef{
F: v.Values,
F: v.Sizes,
},
}
}

View File

@ -1920,7 +1920,7 @@ func (f *File) SetSheetDimension(sheet string, rangeRef string) error {
return err
}
// SetSheetDimension provides the method to get the used range of the worksheet.
// GetSheetDimension provides the method to get the used range of the worksheet.
func (f *File) GetSheetDimension(sheet string) (string, error) {
var ref string
ws, err := f.workSheetReader(sheet)

View File

@ -579,7 +579,6 @@ type ChartMarker struct {
// ChartLine directly maps the format settings of the chart line.
type ChartLine struct {
Color string
Smooth bool
Width float64
}
@ -588,7 +587,9 @@ type ChartLine struct {
type ChartSeries struct {
Name string
Categories string
Sizes string
Values string
Fill Fill
Line ChartLine
Marker ChartMarker
}