浏览代码

- Add 3D column and 3D 100% stacked column chart support, relate issue #160;
- go test updated

Ri Xu 8 年之前
父节点
当前提交
50cdaed5a3
共有 2 个文件被更改,包括 136 次插入88 次删除
  1. 133 87
      chart.go
  2. 3 1
      excelize_test.go

+ 133 - 87
chart.go

@@ -9,62 +9,73 @@ import (
 
 // This section defines the currently supported chart types.
 const (
-	Bar        = "bar"
-	BarStacked = "barStacked"
-	Bar3D      = "bar3D"
-	Doughnut   = "doughnut"
-	Line       = "line"
-	Pie        = "pie"
-	Pie3D      = "pie3D"
-	Radar      = "radar"
-	Scatter    = "scatter"
+	Bar                 = "bar"
+	BarStacked          = "barStacked"
+	Bar3D               = "bar3D"
+	Bar3DColumn         = "bar3DColumn"
+	Bar3DPercentStacked = "bar3DPercentStacked"
+	Doughnut            = "doughnut"
+	Line                = "line"
+	Pie                 = "pie"
+	Pie3D               = "pie3D"
+	Radar               = "radar"
+	Scatter             = "scatter"
 )
 
 // This section defines the default value of chart properties.
 var (
 	chartView3DRotX = map[string]int{
-		Bar:        0,
-		BarStacked: 0,
-		Bar3D:      15,
-		Doughnut:   0,
-		Line:       0,
-		Pie:        0,
-		Pie3D:      30,
-		Radar:      0,
-		Scatter:    0,
+		Bar:                 0,
+		BarStacked:          0,
+		Bar3D:               15,
+		Bar3DColumn:         15,
+		Bar3DPercentStacked: 15,
+		Doughnut:            0,
+		Line:                0,
+		Pie:                 0,
+		Pie3D:               30,
+		Radar:               0,
+		Scatter:             0,
 	}
 	chartView3DRotY = map[string]int{
-		Bar:        0,
-		BarStacked: 0,
-		Bar3D:      20,
-		Doughnut:   0,
-		Line:       0,
-		Pie:        0,
-		Pie3D:      0,
-		Radar:      0,
-		Scatter:    0,
+		Bar:                 0,
+		BarStacked:          0,
+		Bar3D:               20,
+		Bar3DColumn:         20,
+		Bar3DPercentStacked: 20,
+		Doughnut:            0,
+		Line:                0,
+		Pie:                 0,
+		Pie3D:               0,
+		Radar:               0,
+		Scatter:             0,
 	}
 	chartView3DDepthPercent = map[string]int{
-		Bar:        100,
-		BarStacked: 100,
-		Bar3D:      100,
-		Doughnut:   100,
-		Line:       100,
-		Pie:        100,
-		Pie3D:      100,
-		Radar:      100,
-		Scatter:    100,
+		Bar:                 100,
+		BarStacked:          100,
+		Bar3D:               100,
+		Bar3DColumn:         100,
+		Bar3DPercentStacked: 100,
+		Doughnut:            100,
+		Line:                100,
+		Pie:                 100,
+		Pie3D:               100,
+		Radar:               100,
+		Scatter:             100,
 	}
 	chartView3DRAngAx = map[string]int{
-		Bar:        0,
-		BarStacked: 0,
-		Bar3D:      1,
-		Doughnut:   0,
-		Line:       0,
-		Pie:        0,
-		Pie3D:      0,
-		Radar:      0,
-		Scatter:    0}
+		Bar:                 0,
+		BarStacked:          0,
+		Bar3D:               1,
+		Bar3DColumn:         1,
+		Bar3DPercentStacked: 1,
+		Doughnut:            0,
+		Line:                0,
+		Pie:                 0,
+		Pie3D:               0,
+		Radar:               0,
+		Scatter:             0,
+	}
 	chartLegendPosition = map[string]string{
 		"bottom":    "b",
 		"left":      "l",
@@ -72,6 +83,27 @@ var (
 		"top":       "t",
 		"top_right": "tr",
 	}
+	chartValAxNumFmtFormatCode = map[string]string{
+		Bar:                 "General",
+		BarStacked:          "General",
+		Bar3D:               "General",
+		Bar3DColumn:         "General",
+		Bar3DPercentStacked: "0%",
+		Doughnut:            "General",
+		Line:                "General",
+		Pie:                 "General",
+		Pie3D:               "General",
+		Radar:               "General",
+		Scatter:             "General",
+	}
+	plotAreaChartGrouping = map[string]string{
+		Bar:                 "clustered",
+		BarStacked:          "stacked",
+		Bar3D:               "clustered",
+		Bar3DColumn:         "standard",
+		Bar3DPercentStacked: "percentStacked",
+		Line:                "standard",
+	}
 )
 
 // parseFormatChartSet provides function to parse the format settings of the
@@ -133,17 +165,19 @@ func parseFormatChartSet(formatSet string) *formatChart {
 //
 // The following shows the type of chart supported by excelize:
 //
-//     Type       | Chart
-//    ------------+----------------
-//     bar        | bar chart
-//     barStacked | stacked bar chart
-//     bar3D      | 3D bar chart
-//     doughnut   | doughnut chart
-//     line       | line chart
-//     pie        | pie chart
-//     pie3D      | 3D pie chart
-//     radar      | radar chart
-//     scatter    | scatter chart
+//     Type                | Chart
+//    ---------------------+---------------------------
+//     bar                 | bar chart
+//     barStacked          | stacked bar chart
+//     bar3D               | 3D bar chart
+//	   bar3DColumn 		   | 3D column bar chart
+// 	   bar3DPercentStacked | 3D 100% stacked bar chart
+//     doughnut            | doughnut chart
+//     line                | line chart
+//     pie                 | pie chart
+//     pie3D               | 3D pie chart
+//     radar               | radar chart
+//     scatter             | scatter chart
 //
 // In Excel a chart series is a collection of information that defines which data is plotted such as values, axis labels and formatting.
 //
@@ -389,15 +423,17 @@ func (f *File) addChart(formatSet *formatChart) {
 		},
 	}
 	plotAreaFunc := map[string]func(*formatChart) *cPlotArea{
-		Bar:        f.drawBarChart,
-		BarStacked: f.drawBarChart,
-		Bar3D:      f.drawBarChart,
-		Doughnut:   f.drawDoughnutChart,
-		Line:       f.drawLineChart,
-		Pie3D:      f.drawPie3DChart,
-		Pie:        f.drawPieChart,
-		Radar:      f.drawRadarChart,
-		Scatter:    f.drawScatterChart,
+		Bar:                 f.drawBarChart,
+		BarStacked:          f.drawBarChart,
+		Bar3D:               f.drawBarChart,
+		Bar3DColumn:         f.drawBarChart,
+		Bar3DPercentStacked: f.drawBarChart,
+		Doughnut:            f.drawDoughnutChart,
+		Line:                f.drawLineChart,
+		Pie3D:               f.drawPie3DChart,
+		Pie:                 f.drawPieChart,
+		Radar:               f.drawRadarChart,
+		Scatter:             f.drawScatterChart,
 	}
 	xlsxChartSpace.Chart.PlotArea = plotAreaFunc[formatSet.Type](formatSet)
 
@@ -426,12 +462,12 @@ func (f *File) drawBarChart(formatSet *formatChart) *cPlotArea {
 			{Val: 753999904},
 		},
 	}
+	c.Grouping.Val = plotAreaChartGrouping[formatSet.Type]
 	if formatSet.Type == "barStacked" {
-		c.Grouping.Val = "stacked"
 		c.Overlap = &attrValInt{Val: 100}
 	}
-	catAx := f.drawPlotAreaCatAx()
-	valAx := f.drawPlotAreaValAx()
+	catAx := f.drawPlotAreaCatAx(formatSet)
+	valAx := f.drawPlotAreaValAx(formatSet)
 	charts := map[string]*cPlotArea{
 		"bar": {
 			BarChart: &c,
@@ -448,6 +484,16 @@ func (f *File) drawBarChart(formatSet *formatChart) *cPlotArea {
 			CatAx:      catAx,
 			ValAx:      valAx,
 		},
+		"bar3DColumn": {
+			Bar3DChart: &c,
+			CatAx:      catAx,
+			ValAx:      valAx,
+		},
+		"bar3DPercentStacked": {
+			Bar3DChart: &c,
+			CatAx:      catAx,
+			ValAx:      valAx,
+		},
 	}
 	return charts[formatSet.Type]
 }
@@ -472,7 +518,7 @@ func (f *File) drawLineChart(formatSet *formatChart) *cPlotArea {
 	return &cPlotArea{
 		LineChart: &cCharts{
 			Grouping: &attrValString{
-				Val: "standard",
+				Val: plotAreaChartGrouping[formatSet.Type],
 			},
 			VaryColors: &attrValBool{
 				Val: false,
@@ -487,8 +533,8 @@ func (f *File) drawLineChart(formatSet *formatChart) *cPlotArea {
 				{Val: 753999904},
 			},
 		},
-		CatAx: f.drawPlotAreaCatAx(),
-		ValAx: f.drawPlotAreaValAx(),
+		CatAx: f.drawPlotAreaCatAx(formatSet),
+		ValAx: f.drawPlotAreaValAx(formatSet),
 	}
 }
 
@@ -536,8 +582,8 @@ func (f *File) drawRadarChart(formatSet *formatChart) *cPlotArea {
 				{Val: 753999904},
 			},
 		},
-		CatAx: f.drawPlotAreaCatAx(),
-		ValAx: f.drawPlotAreaValAx(),
+		CatAx: f.drawPlotAreaCatAx(formatSet),
+		ValAx: f.drawPlotAreaValAx(formatSet),
 	}
 }
 
@@ -559,8 +605,8 @@ func (f *File) drawScatterChart(formatSet *formatChart) *cPlotArea {
 				{Val: 753999904},
 			},
 		},
-		CatAx: f.drawPlotAreaCatAx(),
-		ValAx: f.drawPlotAreaValAx(),
+		CatAx: f.drawPlotAreaCatAx(formatSet),
+		ValAx: f.drawPlotAreaValAx(formatSet),
 	}
 }
 
@@ -608,7 +654,7 @@ func (f *File) drawChartSeriesSpPr(i int, formatSet *formatChart) *cSpPr {
 			},
 		},
 	}
-	chartSeriesSpPr := map[string]*cSpPr{Bar: nil, BarStacked: nil, Bar3D: nil, Doughnut: nil, Line: spPrLine, Pie: nil, Pie3D: nil, Radar: nil, Scatter: spPrScatter}
+	chartSeriesSpPr := map[string]*cSpPr{Bar: nil, BarStacked: nil, Bar3D: nil, Bar3DColumn: nil, Bar3DPercentStacked: nil, Doughnut: nil, Line: spPrLine, Pie: nil, Pie3D: nil, Radar: nil, Scatter: spPrScatter}
 	return chartSeriesSpPr[formatSet.Type]
 }
 
@@ -637,7 +683,7 @@ func (f *File) drawChartSeriesDPt(i int, formatSet *formatChart) []*cDPt {
 			},
 		},
 	}}
-	chartSeriesDPt := map[string][]*cDPt{Bar: nil, BarStacked: nil, Bar3D: nil, Doughnut: nil, Line: nil, Pie: dpt, Pie3D: dpt, Radar: nil, Scatter: nil}
+	chartSeriesDPt := map[string][]*cDPt{Bar: nil, BarStacked: nil, Bar3D: nil, Bar3DColumn: nil, Bar3DPercentStacked: nil, Doughnut: nil, Line: nil, Pie: dpt, Pie3D: dpt, Radar: nil, Scatter: nil}
 	return chartSeriesDPt[formatSet.Type]
 }
 
@@ -649,7 +695,7 @@ func (f *File) drawChartSeriesCat(v formatChartSeries, formatSet *formatChart) *
 			F: v.Categories,
 		},
 	}
-	chartSeriesCat := map[string]*cCat{Bar: cat, BarStacked: cat, Bar3D: cat, Doughnut: cat, Line: cat, Pie: cat, Pie3D: cat, Radar: cat, Scatter: nil}
+	chartSeriesCat := map[string]*cCat{Bar: cat, BarStacked: cat, Bar3D: cat, Bar3DColumn: cat, Bar3DPercentStacked: cat, Doughnut: cat, Line: cat, Pie: cat, Pie3D: cat, Radar: cat, Scatter: nil}
 	return chartSeriesCat[formatSet.Type]
 }
 
@@ -661,7 +707,7 @@ func (f *File) drawChartSeriesVal(v formatChartSeries, formatSet *formatChart) *
 			F: v.Values,
 		},
 	}
-	chartSeriesVal := map[string]*cVal{Bar: val, BarStacked: val, Bar3D: val, Doughnut: val, Line: val, Pie: val, Pie3D: val, Radar: val, Scatter: nil}
+	chartSeriesVal := map[string]*cVal{Bar: val, BarStacked: val, Bar3D: val, Bar3DColumn: val, Bar3DPercentStacked: val, Doughnut: val, Line: val, Pie: val, Pie3D: val, Radar: val, Scatter: nil}
 	return chartSeriesVal[formatSet.Type]
 }
 
@@ -687,7 +733,7 @@ func (f *File) drawChartSeriesMarker(i int, formatSet *formatChart) *cMarker {
 			},
 		},
 	}
-	chartSeriesMarker := map[string]*cMarker{Bar: nil, BarStacked: nil, Bar3D: nil, Doughnut: nil, Line: nil, Pie: nil, Pie3D: nil, Radar: nil, Scatter: marker}
+	chartSeriesMarker := map[string]*cMarker{Bar: nil, BarStacked: nil, Bar3D: nil, Bar3DColumn: nil, Bar3DPercentStacked: nil, Doughnut: nil, Line: nil, Pie: nil, Pie3D: nil, Radar: nil, Scatter: marker}
 	return chartSeriesMarker[formatSet.Type]
 }
 
@@ -699,7 +745,7 @@ func (f *File) drawChartSeriesXVal(v formatChartSeries, formatSet *formatChart)
 			F: v.Categories,
 		},
 	}
-	chartSeriesXVal := map[string]*cCat{Bar: nil, BarStacked: nil, Bar3D: nil, Doughnut: nil, Line: nil, Pie: nil, Pie3D: nil, Radar: nil, Scatter: cat}
+	chartSeriesXVal := map[string]*cCat{Bar: nil, BarStacked: nil, Bar3D: nil, Bar3DColumn: nil, Bar3DPercentStacked: nil, Doughnut: nil, Line: nil, Pie: nil, Pie3D: nil, Radar: nil, Scatter: cat}
 	return chartSeriesXVal[formatSet.Type]
 }
 
@@ -711,7 +757,7 @@ func (f *File) drawChartSeriesYVal(v formatChartSeries, formatSet *formatChart)
 			F: v.Values,
 		},
 	}
-	chartSeriesYVal := map[string]*cVal{Bar: nil, BarStacked: nil, Bar3D: nil, Doughnut: nil, Line: nil, Pie: nil, Pie3D: nil, Radar: nil, Scatter: val}
+	chartSeriesYVal := map[string]*cVal{Bar: nil, BarStacked: nil, Bar3D: nil, Bar3DColumn: nil, Bar3DPercentStacked: nil, Doughnut: nil, Line: nil, Pie: nil, Pie3D: nil, Radar: nil, Scatter: val}
 	return chartSeriesYVal[formatSet.Type]
 }
 
@@ -733,12 +779,12 @@ func (f *File) drawChartDLbls(formatSet *formatChart) *cDLbls {
 // format sets.
 func (f *File) drawChartSeriesDLbls(formatSet *formatChart) *cDLbls {
 	dLbls := f.drawChartDLbls(formatSet)
-	chartSeriesDLbls := map[string]*cDLbls{Bar: dLbls, BarStacked: dLbls, Bar3D: dLbls, Doughnut: dLbls, Line: dLbls, Pie: dLbls, Pie3D: dLbls, Radar: dLbls, Scatter: nil}
+	chartSeriesDLbls := map[string]*cDLbls{Bar: dLbls, BarStacked: dLbls, Bar3D: dLbls, Bar3DColumn: dLbls, Bar3DPercentStacked: dLbls, Doughnut: dLbls, Line: dLbls, Pie: dLbls, Pie3D: dLbls, Radar: dLbls, Scatter: nil}
 	return chartSeriesDLbls[formatSet.Type]
 }
 
 // drawPlotAreaCatAx provides function to draw the c:catAx element.
-func (f *File) drawPlotAreaCatAx() []*cAxs {
+func (f *File) drawPlotAreaCatAx(formatSet *formatChart) []*cAxs {
 	return []*cAxs{
 		{
 			AxID: &attrValInt{Val: 754001152},
@@ -766,8 +812,8 @@ func (f *File) drawPlotAreaCatAx() []*cAxs {
 	}
 }
 
-// drawPlotAreaCatAx provides function to draw the c:valAx element.
-func (f *File) drawPlotAreaValAx() []*cAxs {
+// drawPlotAreaValAx provides function to draw the c:valAx element.
+func (f *File) drawPlotAreaValAx(formatSet *formatChart) []*cAxs {
 	return []*cAxs{
 		{
 			AxID: &attrValInt{Val: 753999904},
@@ -777,7 +823,7 @@ func (f *File) drawPlotAreaValAx() []*cAxs {
 			Delete: &attrValBool{Val: false},
 			AxPos:  &attrValString{Val: "l"},
 			NumFmt: &cNumFmt{
-				FormatCode:   "General",
+				FormatCode:   chartValAxNumFmtFormatCode[formatSet.Type],
 				SourceLinked: true,
 			},
 			MajorTickMark: &attrValString{Val: "none"},

+ 3 - 1
excelize_test.go

@@ -819,7 +819,9 @@ func TestAddChart(t *testing.T) {
 	xlsx.AddChart("Sheet1", "X30", `{"type":"pie","series":[{"name":"=Sheet1!$A$30","categories":"=Sheet1!$B$29:$D$29","values":"=Sheet1!$B$30:$D$30"}],"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":"bottom","show_legend_key":false},"title":{"name":"Fruit Pie Chart"},"plotarea":{"show_bubble_size":true,"show_cat_name":false,"show_leader_lines":false,"show_percent":true,"show_series_name":false,"show_val":false},"show_blanks_as":"gap"}`)
 	xlsx.AddChart("Sheet2", "P1", `{"type":"radar","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"}],"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":"top_right","show_legend_key":false},"title":{"name":"Fruit Radar 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":"span"}`)
 	xlsx.AddChart("Sheet2", "X1", `{"type":"scatter","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"}],"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":"bottom","show_legend_key":false},"title":{"name":"Fruit Scatter 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"}`)
-	xlsx.AddChart("Sheet2", "P16", `{"type":"barStacked","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"}],"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":"Fruit Bar 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"}`)
+	xlsx.AddChart("Sheet2", "P16", `{"type":"barStacked","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"}],"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":"Fruit Stacked Column 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"}`)
+	xlsx.AddChart("Sheet2", "X17", `{"type":"bar3DColumn","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"}],"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":"Fruit 3D Column 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"}`)
+	xlsx.AddChart("Sheet2", "P32", `{"type":"bar3DPercentStacked","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"}],"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":"Fruit 3D 100% Stacked Column 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"}`)
 	// Save xlsx file by the given path.
 	err = xlsx.SaveAs("./test/Workbook_addchart.xlsx")
 	if err != nil {