Skip to content

Commit e77c462

Browse files
committed
Support to create bubble and 3D bubble chart
1 parent a335be7 commit e77c462

File tree

3 files changed

+74
-16
lines changed

3 files changed

+74
-16
lines changed

chart.go

+64-15
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ const (
6969
WireframeSurface3D = "wireframeSurface3D"
7070
Contour = "contour"
7171
WireframeContour = "wireframeContour"
72+
Bubble = "bubble"
73+
Bubble3D = "bubble3D"
7274
)
7375

7476
// This section defines the default value of chart properties.
@@ -228,6 +230,8 @@ var (
228230
WireframeSurface3D: 100,
229231
Contour: 100,
230232
WireframeContour: 100,
233+
Bubble: 100,
234+
Bubble3D: 100,
231235
}
232236
chartView3DPerspective = map[string]int{
233237
Contour: 0,
@@ -283,6 +287,8 @@ var (
283287
Surface3D: 0,
284288
WireframeSurface3D: 0,
285289
Contour: 0,
290+
Bubble: 0,
291+
Bubble3D: 0,
286292
}
287293
chartLegendPosition = map[string]string{
288294
"bottom": "b",
@@ -342,6 +348,8 @@ var (
342348
WireframeSurface3D: "General",
343349
Contour: "General",
344350
WireframeContour: "General",
351+
Bubble: "General",
352+
Bubble3D: "General",
345353
}
346354
chartValAxCrossBetween = map[string]string{
347355
Area: "midCat",
@@ -394,6 +402,8 @@ var (
394402
WireframeSurface3D: "midCat",
395403
Contour: "midCat",
396404
WireframeContour: "midCat",
405+
Bubble: "midCat",
406+
Bubble3D: "midCat",
397407
}
398408
plotAreaChartGrouping = map[string]string{
399409
Area: "standard",
@@ -611,7 +621,9 @@ func parseFormatChartSet(formatSet string) (*formatChart, error) {
611621
// surface3D | 3D surface chart
612622
// wireframeSurface3D | 3D wireframe surface chart
613623
// contour | contour chart
614-
// wireframeContour | wireframe contour
624+
// wireframeContour | wireframe contour chart
625+
// bubble | bubble chart
626+
// bubble3D | 3D bubble chart
615627
//
616628
// In Excel a chart series is a collection of information that defines which data is plotted such as values, axis labels and formatting.
617629
//
@@ -935,6 +947,8 @@ func (f *File) addChart(formatSet *formatChart) {
935947
WireframeSurface3D: f.drawSurface3DChart,
936948
Contour: f.drawSurfaceChart,
937949
WireframeContour: f.drawSurfaceChart,
950+
Bubble: f.drawBaseChart,
951+
Bubble3D: f.drawBaseChart,
938952
}
939953
xlsxChartSpace.Chart.PlotArea = plotAreaFunc[formatSet.Type](formatSet)
940954

@@ -965,12 +979,13 @@ func (f *File) drawBaseChart(formatSet *formatChart) *cPlotArea {
965979
},
966980
}
967981
var ok bool
968-
c.BarDir.Val, ok = plotAreaChartBarDir[formatSet.Type]
969-
if !ok {
982+
if c.BarDir.Val, ok = plotAreaChartBarDir[formatSet.Type]; !ok {
970983
c.BarDir = nil
971984
}
972-
c.Grouping.Val = plotAreaChartGrouping[formatSet.Type]
973-
if formatSet.Type == "colStacked" || formatSet.Type == "barStacked" || formatSet.Type == "barPercentStacked" || formatSet.Type == "colPercentStacked" || formatSet.Type == "areaPercentStacked" {
985+
if c.Grouping.Val, ok = plotAreaChartGrouping[formatSet.Type]; !ok {
986+
c.Grouping = nil
987+
}
988+
if strings.HasSuffix(formatSet.Type, "Stacked") {
974989
c.Overlap = &attrValInt{Val: 100}
975990
}
976991
catAx := f.drawPlotAreaCatAx(formatSet)
@@ -1176,6 +1191,16 @@ func (f *File) drawBaseChart(formatSet *formatChart) *cPlotArea {
11761191
CatAx: catAx,
11771192
ValAx: valAx,
11781193
},
1194+
"bubble": {
1195+
BubbleChart: &c,
1196+
CatAx: catAx,
1197+
ValAx: valAx,
1198+
},
1199+
"bubble3D": {
1200+
BubbleChart: &c,
1201+
CatAx: catAx,
1202+
ValAx: valAx,
1203+
},
11791204
}
11801205
return charts[formatSet.Type]
11811206
}
@@ -1381,14 +1406,16 @@ func (f *File) drawChartSeries(formatSet *formatChart) *[]cSer {
13811406
F: formatSet.Series[k].Name,
13821407
},
13831408
},
1384-
SpPr: f.drawChartSeriesSpPr(k, formatSet),
1385-
Marker: f.drawChartSeriesMarker(k, formatSet),
1386-
DPt: f.drawChartSeriesDPt(k, formatSet),
1387-
DLbls: f.drawChartSeriesDLbls(formatSet),
1388-
Cat: f.drawChartSeriesCat(formatSet.Series[k], formatSet),
1389-
Val: f.drawChartSeriesVal(formatSet.Series[k], formatSet),
1390-
XVal: f.drawChartSeriesXVal(formatSet.Series[k], formatSet),
1391-
YVal: f.drawChartSeriesYVal(formatSet.Series[k], formatSet),
1409+
SpPr: f.drawChartSeriesSpPr(k, formatSet),
1410+
Marker: f.drawChartSeriesMarker(k, formatSet),
1411+
DPt: f.drawChartSeriesDPt(k, formatSet),
1412+
DLbls: f.drawChartSeriesDLbls(formatSet),
1413+
Cat: f.drawChartSeriesCat(formatSet.Series[k], formatSet),
1414+
Val: f.drawChartSeriesVal(formatSet.Series[k], formatSet),
1415+
XVal: f.drawChartSeriesXVal(formatSet.Series[k], formatSet),
1416+
YVal: f.drawChartSeriesYVal(formatSet.Series[k], formatSet),
1417+
BubbleSize: f.drawCharSeriesBubbleSize(formatSet.Series[k], formatSet),
1418+
Bubble3D: f.drawCharSeriesBubble3D(formatSet),
13921419
})
13931420
}
13941421
return &ser
@@ -1525,10 +1552,32 @@ func (f *File) drawChartSeriesYVal(v formatChartSeries, formatSet *formatChart)
15251552
F: v.Values,
15261553
},
15271554
}
1528-
chartSeriesYVal := map[string]*cVal{Scatter: val}
1555+
chartSeriesYVal := map[string]*cVal{Scatter: val, Bubble: val, Bubble3D: val}
15291556
return chartSeriesYVal[formatSet.Type]
15301557
}
15311558

1559+
// drawCharSeriesBubbleSize provides a function to draw the c:bubbleSize
1560+
// element by given chart series and format sets.
1561+
func (f *File) drawCharSeriesBubbleSize(v formatChartSeries, formatSet *formatChart) *cVal {
1562+
if _, ok := map[string]bool{Bubble: true, Bubble3D: true}[formatSet.Type]; !ok {
1563+
return nil
1564+
}
1565+
return &cVal{
1566+
NumRef: &cNumRef{
1567+
F: v.Values,
1568+
},
1569+
}
1570+
}
1571+
1572+
// drawCharSeriesBubble3D provides a function to draw the c:bubble3D element
1573+
// by given format sets.
1574+
func (f *File) drawCharSeriesBubble3D(formatSet *formatChart) *attrValBool {
1575+
if _, ok := map[string]bool{Bubble3D: true}[formatSet.Type]; !ok {
1576+
return nil
1577+
}
1578+
return &attrValBool{Val: true}
1579+
}
1580+
15321581
// drawChartDLbls provides a function to draw the c:dLbls element by given
15331582
// format sets.
15341583
func (f *File) drawChartDLbls(formatSet *formatChart) *cDLbls {
@@ -1547,7 +1596,7 @@ func (f *File) drawChartDLbls(formatSet *formatChart) *cDLbls {
15471596
// given format sets.
15481597
func (f *File) drawChartSeriesDLbls(formatSet *formatChart) *cDLbls {
15491598
dLbls := f.drawChartDLbls(formatSet)
1550-
chartSeriesDLbls := map[string]*cDLbls{Scatter: nil, Surface3D: nil, WireframeSurface3D: nil, Contour: nil, WireframeContour: nil}
1599+
chartSeriesDLbls := map[string]*cDLbls{Scatter: nil, Surface3D: nil, WireframeSurface3D: nil, Contour: nil, WireframeContour: nil, Bubble: nil, Bubble3D: nil}
15511600
if _, ok := chartSeriesDLbls[formatSet.Type]; ok {
15521601
return nil
15531602
}

chart_test.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,12 @@ func TestAddChart(t *testing.T) {
174174
// surface series chart
175175
assert.NoError(t, f.AddChart("Sheet2", "AV1", `{"type":"surface3D","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"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"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Surface Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","y_axis":{"major_grid_lines":true}}`))
176176
assert.NoError(t, f.AddChart("Sheet2", "AV16", `{"type":"wireframeSurface3D","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"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"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"3D Wireframe Surface Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","y_axis":{"major_grid_lines":true}}`))
177-
assert.NoError(t, f.AddChart("Sheet2", "AV30", `{"type":"contour","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"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"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Contour Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
177+
assert.NoError(t, f.AddChart("Sheet2", "AV32", `{"type":"contour","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"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"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Contour Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
178178
assert.NoError(t, f.AddChart("Sheet2", "BD1", `{"type":"wireframeContour","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"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"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Wireframe Contour Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
179+
// bubble chart
180+
assert.NoError(t, f.AddChart("Sheet2", "BD16", `{"type":"bubble","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"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"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Bubble Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`))
181+
assert.NoError(t, f.AddChart("Sheet2", "BD32", `{"type":"bubble3D","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"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"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Bubble 3D Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero","x_axis":{"major_grid_lines":true},"y_axis":{"major_grid_lines":true}}`))
179182
assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddChart.xlsx")))
183+
184+
assert.EqualError(t, f.AddChart("Sheet2", "BD32", `{"type":"unknown","series":[{"name":"Sheet1!$A$30","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$30:$D$30"},{"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"},{"name":"Sheet1!$A$34","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$34:$D$34"},{"name":"Sheet1!$A$35","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$35:$D$35"},{"name":"Sheet1!$A$36","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$36:$D$36"},{"name":"Sheet1!$A$37","categories":"Sheet1!$B$29:$D$29","values":"Sheet1!$B$37:$D$37"}],"format":{"x_scale":1.0,"y_scale":1.0,"x_offset":15,"y_offset":10,"print_obj":true,"lock_aspect_ratio":false,"locked":false},"legend":{"position":"left","show_legend_key":false},"title":{"name":"Bubble 3D Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":true,"show_val":true},"show_blanks_as":"zero"}`), "unsupported chart type unknown")
180185
}

xmlChart.go

+4
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ type cPlotArea struct {
306306
Area3DChart *cCharts `xml:"area3DChart"`
307307
BarChart *cCharts `xml:"barChart"`
308308
Bar3DChart *cCharts `xml:"bar3DChart"`
309+
BubbleChart *cCharts `xml:"bubbleChart"`
309310
DoughnutChart *cCharts `xml:"doughnutChart"`
310311
LineChart *cCharts `xml:"lineChart"`
311312
PieChart *cCharts `xml:"pieChart"`
@@ -323,6 +324,7 @@ type cPlotArea struct {
323324
// cCharts specifies the common element of the chart.
324325
type cCharts struct {
325326
BarDir *attrValString `xml:"barDir"`
327+
BubbleScale *attrValFloat `xml:"bubbleScale"`
326328
Grouping *attrValString `xml:"grouping"`
327329
RadarStyle *attrValString `xml:"radarStyle"`
328330
ScatterStyle *attrValString `xml:"scatterStyle"`
@@ -395,6 +397,8 @@ type cSer struct {
395397
XVal *cCat `xml:"xVal"`
396398
YVal *cVal `xml:"yVal"`
397399
Smooth *attrValBool `xml:"smooth"`
400+
BubbleSize *cVal `xml:"bubbleSize"`
401+
Bubble3D *attrValBool `xml:"bubble3D"`
398402
}
399403

400404
// cMarker (Marker) directly maps the marker element. This element specifies a

0 commit comments

Comments
 (0)