Skip to content

Commit dfaf418

Browse files
authored
This closes # 1704, support set the data labels position for the chart (#1755)
- Breaking change: remove the Sizes field in the ChartSeries data type - Add new field DataLabelPosition in the ChartSeries data type, support to sets the position of the chart series data label - Add new field BubbleSize in the Chart data type, support set the bubble size in all data series for the bubble chart or 3D bubble chart - Add new exported ChartDataLabelPositionType data type - Update docs and unit test for the AddChart function - Fix a v2.7.1 regression bug, the bubble is hidden in the bubble or 3D bubble chart, commit ID: c2d6707
1 parent 284345e commit dfaf418

File tree

5 files changed

+114
-35
lines changed

5 files changed

+114
-35
lines changed

chart.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -688,11 +688,11 @@ func (opts *Chart) parseTitle() {
688688
//
689689
// Name
690690
// Categories
691-
// Sizes
692691
// Values
693692
// Fill
694693
// Line
695694
// Marker
695+
// DataLabelPosition
696696
//
697697
// Name: Set the name for the series. The name is displayed in the chart legend
698698
// and in the formula bar. The 'Name' property is optional and if it isn't
@@ -703,8 +703,6 @@ func (opts *Chart) parseTitle() {
703703
// the same as the X axis. In most chart types the 'Categories' property is
704704
// optional and the chart will just assume a sequential series from 1..n.
705705
//
706-
// Sizes: This sets the bubble size in a data series.
707-
//
708706
// Values: This is the most important property of a series and is the only
709707
// mandatory option for every chart object. This option links the chart with
710708
// the worksheet data that it displays.
@@ -733,6 +731,8 @@ func (opts *Chart) parseTitle() {
733731
// x
734732
// auto
735733
//
734+
// DataLabelPosition: This sets the position of the chart series data label.
735+
//
736736
// Set properties of the chart legend. The options that can be set are:
737737
//
738738
// Position
@@ -776,11 +776,11 @@ func (opts *Chart) parseTitle() {
776776
// Specifies that each data marker in the series has a different color by
777777
// 'VaryColors'. The default value is true.
778778
//
779-
// Set chart offset, scale, aspect ratio setting and print settings by format,
779+
// Set chart offset, scale, aspect ratio setting and print settings by 'Format',
780780
// same as function 'AddPicture'.
781781
//
782-
// Set the position of the chart plot area by PlotArea. The properties that can
783-
// be set are:
782+
// Set the position of the chart plot area by 'PlotArea'. The properties that
783+
// can be set are:
784784
//
785785
// SecondPlotValues
786786
// ShowBubbleSize
@@ -891,6 +891,15 @@ func (opts *Chart) parseTitle() {
891891
// Set chart size by 'Dimension' property. The 'Dimension' property is optional.
892892
// The default width is 480, and height is 260.
893893
//
894+
// Set the bubble size in all data series for the bubble chart or 3D bubble
895+
// chart by 'BubbleSizes' property. The 'BubbleSizes' property is optional.
896+
// The default width is 100, and the value should be great than 0 and less or
897+
// equal than 300.
898+
//
899+
// Set the doughnut hole size in all data series for the doughnut chart by
900+
// 'HoleSize' property. The 'HoleSize' property is optional. The default width
901+
// is 75, and the value should be great than 0 and less or equal than 90.
902+
//
894903
// combo: Specifies the create a chart that combines two or more chart types in
895904
// a single chart. For example, create a clustered column - line chart with
896905
// data Sheet1!$E$1:$L$15:

chart_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -176,14 +176,14 @@ func TestAddChart(t *testing.T) {
176176
}
177177
series3 := []ChartSeries{{Name: "Sheet1!$A$30", Categories: "Sheet1!$A$30:$D$37", Values: "Sheet1!$B$30:$B$37"}}
178178
series4 := []ChartSeries{
179-
{Name: "Sheet1!$A$30", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$30:$D$30", Sizes: "Sheet1!$B$30:$D$30"},
180-
{Name: "Sheet1!$A$31", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$31:$D$31", Sizes: "Sheet1!$B$31:$D$31"},
181-
{Name: "Sheet1!$A$32", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$32:$D$32", Sizes: "Sheet1!$B$32:$D$32"},
182-
{Name: "Sheet1!$A$33", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$33:$D$33", Sizes: "Sheet1!$B$33:$D$33"},
183-
{Name: "Sheet1!$A$34", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$34:$D$34", Sizes: "Sheet1!$B$34:$D$34"},
184-
{Name: "Sheet1!$A$35", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$35:$D$35", Sizes: "Sheet1!$B$35:$D$35"},
185-
{Name: "Sheet1!$A$36", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$36:$D$36", Sizes: "Sheet1!$B$36:$D$36"},
186-
{Name: "Sheet1!$A$37", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$37:$D$37", Sizes: "Sheet1!$B$37:$D$37"},
179+
{Name: "Sheet1!$A$30", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$30:$D$30", DataLabelPosition: ChartDataLabelsPositionAbove},
180+
{Name: "Sheet1!$A$31", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$31:$D$31", DataLabelPosition: ChartDataLabelsPositionLeft},
181+
{Name: "Sheet1!$A$32", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$32:$D$32", DataLabelPosition: ChartDataLabelsPositionBestFit},
182+
{Name: "Sheet1!$A$33", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$33:$D$33", DataLabelPosition: ChartDataLabelsPositionCenter},
183+
{Name: "Sheet1!$A$34", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$34:$D$34", DataLabelPosition: ChartDataLabelsPositionInsideBase},
184+
{Name: "Sheet1!$A$35", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$35:$D$35", DataLabelPosition: ChartDataLabelsPositionInsideEnd},
185+
{Name: "Sheet1!$A$36", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$36:$D$36", DataLabelPosition: ChartDataLabelsPositionOutsideEnd},
186+
{Name: "Sheet1!$A$37", Categories: "Sheet1!$B$29:$D$29", Values: "Sheet1!$B$37:$D$37", DataLabelPosition: ChartDataLabelsPositionRight},
187187
}
188188
format := GraphicOptions{
189189
ScaleX: defaultDrawingScale,
@@ -265,7 +265,7 @@ func TestAddChart(t *testing.T) {
265265
{sheetName: "Sheet2", cell: "AV32", opts: &Chart{Type: Contour, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "Contour Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
266266
{sheetName: "Sheet2", cell: "BD1", opts: &Chart{Type: WireframeContour, Series: series, Format: format, Legend: legend, Title: []RichTextRun{{Text: "Wireframe Contour Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
267267
// bubble chart
268-
{sheetName: "Sheet2", cell: "BD16", opts: &Chart{Type: Bubble, Series: series4, Format: format, Legend: legend, Title: []RichTextRun{{Text: "Bubble Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero"}},
268+
{sheetName: "Sheet2", cell: "BD16", opts: &Chart{Type: Bubble, Series: series4, Format: format, Legend: legend, Title: []RichTextRun{{Text: "Bubble Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", BubbleSize: 75}},
269269
{sheetName: "Sheet2", cell: "BD32", opts: &Chart{Type: Bubble3D, Series: series4, Format: format, Legend: legend, Title: []RichTextRun{{Text: "Bubble 3D Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}},
270270
// pie of pie chart
271271
{sheetName: "Sheet2", cell: "BD48", opts: &Chart{Type: PieOfPie, Series: series3, Format: format, Legend: legend, Title: []RichTextRun{{Text: "Pie of Pie Chart"}}, PlotArea: plotArea, ShowBlanksAs: "zero", XAxis: ChartAxis{MajorGridLines: true}, YAxis: ChartAxis{MajorGridLines: true}}},

drawing.go

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,9 @@ func (f *File) drawBubbleChart(opts *Chart) *cPlotArea {
659659
},
660660
ValAx: []*cAxs{f.drawPlotAreaCatAx(opts)[0], f.drawPlotAreaValAx(opts)[0]},
661661
}
662+
if opts.BubbleSize > 0 && opts.BubbleSize <= 300 {
663+
plotArea.BubbleChart.BubbleScale = &attrValFloat{Val: float64Ptr(float64(opts.BubbleSize))}
664+
}
662665
return plotArea
663666
}
664667

@@ -710,7 +713,7 @@ func (f *File) drawChartSeries(opts *Chart) *[]cSer {
710713
SpPr: f.drawChartSeriesSpPr(k, opts),
711714
Marker: f.drawChartSeriesMarker(k, opts),
712715
DPt: f.drawChartSeriesDPt(k, opts),
713-
DLbls: f.drawChartSeriesDLbls(opts),
716+
DLbls: f.drawChartSeriesDLbls(k, opts),
714717
InvertIfNegative: &attrValBool{Val: boolPtr(false)},
715718
Cat: f.drawChartSeriesCat(opts.Series[k], opts),
716719
Smooth: &attrValBool{Val: boolPtr(opts.Series[k].Line.Smooth)},
@@ -885,12 +888,12 @@ func (f *File) drawChartSeriesYVal(v ChartSeries, opts *Chart) *cVal {
885888
// drawCharSeriesBubbleSize provides a function to draw the c:bubbleSize
886889
// element by given chart series and format sets.
887890
func (f *File) drawCharSeriesBubbleSize(v ChartSeries, opts *Chart) *cVal {
888-
if _, ok := map[ChartType]bool{Bubble: true, Bubble3D: true}[opts.Type]; !ok || v.Sizes == "" {
891+
if _, ok := map[ChartType]bool{Bubble: true, Bubble3D: true}[opts.Type]; !ok {
889892
return nil
890893
}
891894
return &cVal{
892895
NumRef: &cNumRef{
893-
F: v.Sizes,
896+
F: v.Values,
894897
},
895898
}
896899
}
@@ -932,16 +935,33 @@ func (f *File) drawChartDLbls(opts *Chart) *cDLbls {
932935
}
933936
}
934937

938+
// inSupportedChartDataLabelsPositionType provides a method to check if an
939+
// element is present in an array, and return the index of its location,
940+
// otherwise return -1.
941+
func inSupportedChartDataLabelsPositionType(a []ChartDataLabelPositionType, x ChartDataLabelPositionType) int {
942+
for idx, n := range a {
943+
if x == n {
944+
return idx
945+
}
946+
}
947+
return -1
948+
}
949+
935950
// drawChartSeriesDLbls provides a function to draw the c:dLbls element by
936951
// given format sets.
937-
func (f *File) drawChartSeriesDLbls(opts *Chart) *cDLbls {
952+
func (f *File) drawChartSeriesDLbls(i int, opts *Chart) *cDLbls {
938953
dLbls := f.drawChartDLbls(opts)
939954
chartSeriesDLbls := map[ChartType]*cDLbls{
940-
Scatter: nil, Surface3D: nil, WireframeSurface3D: nil, Contour: nil, WireframeContour: nil, Bubble: nil, Bubble3D: nil,
955+
Scatter: nil, Surface3D: nil, WireframeSurface3D: nil, Contour: nil, WireframeContour: nil,
941956
}
942957
if _, ok := chartSeriesDLbls[opts.Type]; ok {
943958
return nil
944959
}
960+
if types, ok := supportedChartDataLabelsPosition[opts.Type]; ok && opts.Series[i].DataLabelPosition != ChartDataLabelsPositionUnset {
961+
if inSupportedChartDataLabelsPositionType(types, opts.Series[i].DataLabelPosition) != -1 {
962+
dLbls.DLblPos = &attrValString{Val: stringPtr(chartDataLabelsPositionTypes[opts.Series[i].DataLabelPosition])}
963+
}
964+
}
945965
return dLbls
946966
}
947967

templates.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,54 @@ const (
217217
ColorMappingTypeUnset int = -1
218218
)
219219

220+
// ChartDataLabelPositionType is the type of chart data labels position.
221+
type ChartDataLabelPositionType byte
222+
223+
// Chart data labels positions types enumeration.
224+
const (
225+
ChartDataLabelsPositionUnset ChartDataLabelPositionType = iota
226+
ChartDataLabelsPositionBestFit
227+
ChartDataLabelsPositionBelow
228+
ChartDataLabelsPositionCenter
229+
ChartDataLabelsPositionInsideBase
230+
ChartDataLabelsPositionInsideEnd
231+
ChartDataLabelsPositionLeft
232+
ChartDataLabelsPositionOutsideEnd
233+
ChartDataLabelsPositionRight
234+
ChartDataLabelsPositionAbove
235+
)
236+
237+
// chartDataLabelsPositionTypes defined supported chart data labels position
238+
// types.
239+
var chartDataLabelsPositionTypes = map[ChartDataLabelPositionType]string{
240+
ChartDataLabelsPositionBestFit: "bestFit",
241+
ChartDataLabelsPositionBelow: "b",
242+
ChartDataLabelsPositionCenter: "ctr",
243+
ChartDataLabelsPositionInsideBase: "inBase",
244+
ChartDataLabelsPositionInsideEnd: "inEnd",
245+
ChartDataLabelsPositionLeft: "l",
246+
ChartDataLabelsPositionOutsideEnd: "outEnd",
247+
ChartDataLabelsPositionRight: "r",
248+
ChartDataLabelsPositionAbove: "t",
249+
}
250+
251+
// supportedChartDataLabelsPosition defined supported chart data labels position
252+
// types for each type of chart.
253+
var supportedChartDataLabelsPosition = map[ChartType][]ChartDataLabelPositionType{
254+
Bar: {ChartDataLabelsPositionCenter, ChartDataLabelsPositionInsideBase, ChartDataLabelsPositionInsideEnd, ChartDataLabelsPositionOutsideEnd},
255+
BarStacked: {ChartDataLabelsPositionCenter, ChartDataLabelsPositionInsideBase, ChartDataLabelsPositionInsideEnd},
256+
BarPercentStacked: {ChartDataLabelsPositionCenter, ChartDataLabelsPositionInsideBase, ChartDataLabelsPositionInsideEnd},
257+
Col: {ChartDataLabelsPositionCenter, ChartDataLabelsPositionInsideBase, ChartDataLabelsPositionInsideEnd, ChartDataLabelsPositionOutsideEnd},
258+
ColStacked: {ChartDataLabelsPositionCenter, ChartDataLabelsPositionInsideBase, ChartDataLabelsPositionInsideEnd},
259+
ColPercentStacked: {ChartDataLabelsPositionCenter, ChartDataLabelsPositionInsideBase, ChartDataLabelsPositionInsideEnd},
260+
Line: {ChartDataLabelsPositionBelow, ChartDataLabelsPositionCenter, ChartDataLabelsPositionLeft, ChartDataLabelsPositionRight, ChartDataLabelsPositionAbove},
261+
Pie: {ChartDataLabelsPositionBestFit, ChartDataLabelsPositionCenter, ChartDataLabelsPositionInsideEnd, ChartDataLabelsPositionOutsideEnd},
262+
Pie3D: {ChartDataLabelsPositionBestFit, ChartDataLabelsPositionCenter, ChartDataLabelsPositionInsideEnd, ChartDataLabelsPositionOutsideEnd},
263+
Scatter: {ChartDataLabelsPositionBelow, ChartDataLabelsPositionCenter, ChartDataLabelsPositionLeft, ChartDataLabelsPositionRight, ChartDataLabelsPositionAbove},
264+
Bubble: {ChartDataLabelsPositionBelow, ChartDataLabelsPositionCenter, ChartDataLabelsPositionLeft, ChartDataLabelsPositionRight, ChartDataLabelsPositionAbove},
265+
Bubble3D: {ChartDataLabelsPositionBelow, ChartDataLabelsPositionCenter, ChartDataLabelsPositionLeft, ChartDataLabelsPositionRight, ChartDataLabelsPositionAbove},
266+
}
267+
220268
const (
221269
defaultTempFileSST = "sharedStrings"
222270
defaultXMLPathCalcChain = "xl/calcChain.xml"

xmlChart.go

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -482,14 +482,15 @@ type cNumCache struct {
482482
// entire series or the entire chart. It contains child elements that specify
483483
// the specific formatting and positioning settings.
484484
type cDLbls struct {
485-
NumFmt *cNumFmt `xml:"numFmt"`
486-
ShowLegendKey *attrValBool `xml:"showLegendKey"`
487-
ShowVal *attrValBool `xml:"showVal"`
488-
ShowCatName *attrValBool `xml:"showCatName"`
489-
ShowSerName *attrValBool `xml:"showSerName"`
490-
ShowPercent *attrValBool `xml:"showPercent"`
491-
ShowBubbleSize *attrValBool `xml:"showBubbleSize"`
492-
ShowLeaderLines *attrValBool `xml:"showLeaderLines"`
485+
NumFmt *cNumFmt `xml:"numFmt"`
486+
DLblPos *attrValString `xml:"dLblPos"`
487+
ShowLegendKey *attrValBool `xml:"showLegendKey"`
488+
ShowVal *attrValBool `xml:"showVal"`
489+
ShowCatName *attrValBool `xml:"showCatName"`
490+
ShowSerName *attrValBool `xml:"showSerName"`
491+
ShowPercent *attrValBool `xml:"showPercent"`
492+
ShowBubbleSize *attrValBool `xml:"showBubbleSize"`
493+
ShowLeaderLines *attrValBool `xml:"showLeaderLines"`
493494
}
494495

495496
// cLegend (Legend) directly maps the legend element. This element specifies
@@ -577,6 +578,7 @@ type Chart struct {
577578
PlotArea ChartPlotArea
578579
Border ChartLine
579580
ShowBlanksAs string
581+
BubbleSize int
580582
HoleSize int
581583
order int
582584
}
@@ -602,11 +604,11 @@ type ChartLine struct {
602604

603605
// ChartSeries directly maps the format settings of the chart series.
604606
type ChartSeries struct {
605-
Name string
606-
Categories string
607-
Sizes string
608-
Values string
609-
Fill Fill
610-
Line ChartLine
611-
Marker ChartMarker
607+
Name string
608+
Categories string
609+
Values string
610+
Fill Fill
611+
Line ChartLine
612+
Marker ChartMarker
613+
DataLabelPosition ChartDataLabelPositionType
612614
}

0 commit comments

Comments
 (0)