Kaynağa Gözat

Support to create bubble and 3D bubble chart

xuri 6 yıl önce
ebeveyn
işleme
e77c462d3f
3 değiştirilmiş dosya ile 74 ekleme ve 16 silme
  1. 64 15
      chart.go
  2. 6 1
      chart_test.go
  3. 4 0
      xmlChart.go

+ 64 - 15
chart.go

@@ -69,6 +69,8 @@ const (
 	WireframeSurface3D          = "wireframeSurface3D"
 	Contour                     = "contour"
 	WireframeContour            = "wireframeContour"
+	Bubble                      = "bubble"
+	Bubble3D                    = "bubble3D"
 )
 
 // This section defines the default value of chart properties.
@@ -228,6 +230,8 @@ var (
 		WireframeSurface3D:          100,
 		Contour:                     100,
 		WireframeContour:            100,
+		Bubble:                      100,
+		Bubble3D:                    100,
 	}
 	chartView3DPerspective = map[string]int{
 		Contour:          0,
@@ -283,6 +287,8 @@ var (
 		Surface3D:                   0,
 		WireframeSurface3D:          0,
 		Contour:                     0,
+		Bubble:                      0,
+		Bubble3D:                    0,
 	}
 	chartLegendPosition = map[string]string{
 		"bottom":    "b",
@@ -342,6 +348,8 @@ var (
 		WireframeSurface3D:          "General",
 		Contour:                     "General",
 		WireframeContour:            "General",
+		Bubble:                      "General",
+		Bubble3D:                    "General",
 	}
 	chartValAxCrossBetween = map[string]string{
 		Area:                        "midCat",
@@ -394,6 +402,8 @@ var (
 		WireframeSurface3D:          "midCat",
 		Contour:                     "midCat",
 		WireframeContour:            "midCat",
+		Bubble:                      "midCat",
+		Bubble3D:                    "midCat",
 	}
 	plotAreaChartGrouping = map[string]string{
 		Area:                        "standard",
@@ -611,7 +621,9 @@ func parseFormatChartSet(formatSet string) (*formatChart, error) {
 //     surface3D                   | 3D surface chart
 //     wireframeSurface3D          | 3D wireframe surface chart
 //     contour                     | contour chart
-//     wireframeContour            | wireframe contour
+//     wireframeContour            | wireframe contour chart
+//     bubble                      | bubble chart
+//     bubble3D                    | 3D bubble chart
 //
 // In Excel a chart series is a collection of information that defines which data is plotted such as values, axis labels and formatting.
 //
@@ -935,6 +947,8 @@ func (f *File) addChart(formatSet *formatChart) {
 		WireframeSurface3D:          f.drawSurface3DChart,
 		Contour:                     f.drawSurfaceChart,
 		WireframeContour:            f.drawSurfaceChart,
+		Bubble:                      f.drawBaseChart,
+		Bubble3D:                    f.drawBaseChart,
 	}
 	xlsxChartSpace.Chart.PlotArea = plotAreaFunc[formatSet.Type](formatSet)
 
@@ -965,12 +979,13 @@ func (f *File) drawBaseChart(formatSet *formatChart) *cPlotArea {
 		},
 	}
 	var ok bool
-	c.BarDir.Val, ok = plotAreaChartBarDir[formatSet.Type]
-	if !ok {
+	if c.BarDir.Val, ok = plotAreaChartBarDir[formatSet.Type]; !ok {
 		c.BarDir = nil
 	}
-	c.Grouping.Val = plotAreaChartGrouping[formatSet.Type]
-	if formatSet.Type == "colStacked" || formatSet.Type == "barStacked" || formatSet.Type == "barPercentStacked" || formatSet.Type == "colPercentStacked" || formatSet.Type == "areaPercentStacked" {
+	if c.Grouping.Val, ok = plotAreaChartGrouping[formatSet.Type]; !ok {
+		c.Grouping = nil
+	}
+	if strings.HasSuffix(formatSet.Type, "Stacked") {
 		c.Overlap = &attrValInt{Val: 100}
 	}
 	catAx := f.drawPlotAreaCatAx(formatSet)
@@ -1176,6 +1191,16 @@ func (f *File) drawBaseChart(formatSet *formatChart) *cPlotArea {
 			CatAx:      catAx,
 			ValAx:      valAx,
 		},
+		"bubble": {
+			BubbleChart: &c,
+			CatAx:       catAx,
+			ValAx:       valAx,
+		},
+		"bubble3D": {
+			BubbleChart: &c,
+			CatAx:       catAx,
+			ValAx:       valAx,
+		},
 	}
 	return charts[formatSet.Type]
 }
@@ -1381,14 +1406,16 @@ func (f *File) drawChartSeries(formatSet *formatChart) *[]cSer {
 					F: formatSet.Series[k].Name,
 				},
 			},
-			SpPr:   f.drawChartSeriesSpPr(k, formatSet),
-			Marker: f.drawChartSeriesMarker(k, formatSet),
-			DPt:    f.drawChartSeriesDPt(k, formatSet),
-			DLbls:  f.drawChartSeriesDLbls(formatSet),
-			Cat:    f.drawChartSeriesCat(formatSet.Series[k], formatSet),
-			Val:    f.drawChartSeriesVal(formatSet.Series[k], formatSet),
-			XVal:   f.drawChartSeriesXVal(formatSet.Series[k], formatSet),
-			YVal:   f.drawChartSeriesYVal(formatSet.Series[k], formatSet),
+			SpPr:       f.drawChartSeriesSpPr(k, formatSet),
+			Marker:     f.drawChartSeriesMarker(k, formatSet),
+			DPt:        f.drawChartSeriesDPt(k, formatSet),
+			DLbls:      f.drawChartSeriesDLbls(formatSet),
+			Cat:        f.drawChartSeriesCat(formatSet.Series[k], formatSet),
+			Val:        f.drawChartSeriesVal(formatSet.Series[k], formatSet),
+			XVal:       f.drawChartSeriesXVal(formatSet.Series[k], formatSet),
+			YVal:       f.drawChartSeriesYVal(formatSet.Series[k], formatSet),
+			BubbleSize: f.drawCharSeriesBubbleSize(formatSet.Series[k], formatSet),
+			Bubble3D:   f.drawCharSeriesBubble3D(formatSet),
 		})
 	}
 	return &ser
@@ -1525,10 +1552,32 @@ func (f *File) drawChartSeriesYVal(v formatChartSeries, formatSet *formatChart)
 			F: v.Values,
 		},
 	}
-	chartSeriesYVal := map[string]*cVal{Scatter: val}
+	chartSeriesYVal := map[string]*cVal{Scatter: val, Bubble: val, Bubble3D: val}
 	return chartSeriesYVal[formatSet.Type]
 }
 
+// drawCharSeriesBubbleSize provides a function to draw the c:bubbleSize
+// element by given chart series and format sets.
+func (f *File) drawCharSeriesBubbleSize(v formatChartSeries, formatSet *formatChart) *cVal {
+	if _, ok := map[string]bool{Bubble: true, Bubble3D: true}[formatSet.Type]; !ok {
+		return nil
+	}
+	return &cVal{
+		NumRef: &cNumRef{
+			F: v.Values,
+		},
+	}
+}
+
+// drawCharSeriesBubble3D provides a function to draw the c:bubble3D element
+// by given format sets.
+func (f *File) drawCharSeriesBubble3D(formatSet *formatChart) *attrValBool {
+	if _, ok := map[string]bool{Bubble3D: true}[formatSet.Type]; !ok {
+		return nil
+	}
+	return &attrValBool{Val: true}
+}
+
 // drawChartDLbls provides a function to draw the c:dLbls element by given
 // format sets.
 func (f *File) drawChartDLbls(formatSet *formatChart) *cDLbls {
@@ -1547,7 +1596,7 @@ func (f *File) drawChartDLbls(formatSet *formatChart) *cDLbls {
 // given format sets.
 func (f *File) drawChartSeriesDLbls(formatSet *formatChart) *cDLbls {
 	dLbls := f.drawChartDLbls(formatSet)
-	chartSeriesDLbls := map[string]*cDLbls{Scatter: nil, Surface3D: nil, WireframeSurface3D: nil, Contour: nil, WireframeContour: nil}
+	chartSeriesDLbls := map[string]*cDLbls{Scatter: nil, Surface3D: nil, WireframeSurface3D: nil, Contour: nil, WireframeContour: nil, Bubble: nil, Bubble3D: nil}
 	if _, ok := chartSeriesDLbls[formatSet.Type]; ok {
 		return nil
 	}

+ 6 - 1
chart_test.go

@@ -174,7 +174,12 @@ func TestAddChart(t *testing.T) {
 	// surface series chart
 	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}}`))
 	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}}`))
-	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"}`))
+	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"}`))
 	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"}`))
+	// bubble chart
+	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"}`))
+	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}}`))
 	assert.NoError(t, f.SaveAs(filepath.Join("test", "TestAddChart.xlsx")))
+
+	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")
 }

+ 4 - 0
xmlChart.go

@@ -306,6 +306,7 @@ type cPlotArea struct {
 	Area3DChart    *cCharts `xml:"area3DChart"`
 	BarChart       *cCharts `xml:"barChart"`
 	Bar3DChart     *cCharts `xml:"bar3DChart"`
+	BubbleChart    *cCharts `xml:"bubbleChart"`
 	DoughnutChart  *cCharts `xml:"doughnutChart"`
 	LineChart      *cCharts `xml:"lineChart"`
 	PieChart       *cCharts `xml:"pieChart"`
@@ -323,6 +324,7 @@ type cPlotArea struct {
 // cCharts specifies the common element of the chart.
 type cCharts struct {
 	BarDir       *attrValString `xml:"barDir"`
+	BubbleScale  *attrValFloat  `xml:"bubbleScale"`
 	Grouping     *attrValString `xml:"grouping"`
 	RadarStyle   *attrValString `xml:"radarStyle"`
 	ScatterStyle *attrValString `xml:"scatterStyle"`
@@ -395,6 +397,8 @@ type cSer struct {
 	XVal             *cCat        `xml:"xVal"`
 	YVal             *cVal        `xml:"yVal"`
 	Smooth           *attrValBool `xml:"smooth"`
+	BubbleSize       *cVal        `xml:"bubbleSize"`
+	Bubble3D         *attrValBool `xml:"bubble3D"`
 }
 
 // cMarker (Marker) directly maps the marker element. This element specifies a