Skip to content

Commit 8bde1ac

Browse files
authored
Charts Support for Rounded Corners and Trendlines (#2976)
Fix #2968. Fix #2815. Solution largely based on suggestions by @bridgeplayr.
1 parent eb76c3c commit 8bde1ac

File tree

10 files changed

+698
-1
lines changed

10 files changed

+698
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
<?php
2+
3+
use PhpOffice\PhpSpreadsheet\Chart\Axis;
4+
use PhpOffice\PhpSpreadsheet\Chart\Chart;
5+
use PhpOffice\PhpSpreadsheet\Chart\ChartColor;
6+
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
7+
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
8+
use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
9+
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
10+
use PhpOffice\PhpSpreadsheet\Chart\Properties;
11+
use PhpOffice\PhpSpreadsheet\Chart\Title;
12+
use PhpOffice\PhpSpreadsheet\Chart\TrendLine;
13+
use PhpOffice\PhpSpreadsheet\IOFactory;
14+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
15+
16+
require __DIR__ . '/../Header.php';
17+
18+
$spreadsheet = new Spreadsheet();
19+
$dataSheet = $spreadsheet->getActiveSheet();
20+
$dataSheet->setTitle('Data');
21+
// changed data to simulate a trend chart - Xaxis are dates; Yaxis are 3 meausurements from each date
22+
$dataSheet->fromArray(
23+
[
24+
['', 'metric1', 'metric2', 'metric3'],
25+
['=DATEVALUE("2021-01-01")', 12.1, 15.1, 21.1],
26+
['=DATEVALUE("2021-04-01")', 56.2, 73.2, 86.2],
27+
['=DATEVALUE("2021-07-01")', 52.2, 61.2, 69.2],
28+
['=DATEVALUE("2021-10-01")', 30.2, 22.2, 0.2],
29+
['=DATEVALUE("2022-01-01")', 40.1, 38.1, 65.1],
30+
['=DATEVALUE("2022-04-01")', 45.2, 44.2, 96.2],
31+
['=DATEVALUE("2022-07-01")', 52.2, 51.2, 55.2],
32+
['=DATEVALUE("2022-10-01")', 41.2, 72.2, 56.2],
33+
]
34+
);
35+
36+
$dataSheet->getStyle('A2:A9')->getNumberFormat()->setFormatCode(Properties::FORMAT_CODE_DATE_ISO8601);
37+
$dataSheet->getColumnDimension('A')->setAutoSize(true);
38+
$dataSheet->setSelectedCells('A1');
39+
40+
// Set the Labels for each data series we want to plot
41+
// Datatype
42+
// Cell reference for data
43+
// Format Code
44+
// Number of datapoints in series
45+
$dataSeriesLabels = [
46+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Data!$B$1', null, 1),
47+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Data!$C$1', null, 1),
48+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Data!$D$1', null, 1),
49+
];
50+
// Set the X-Axis Labels
51+
// NUMBER, not STRING
52+
// added x-axis values for each of the 3 metrics
53+
// added FORMATE_CODE_NUMBER
54+
// Number of datapoints in series
55+
$xAxisTickValues = [
56+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Data!$A$2:$A$9', Properties::FORMAT_CODE_DATE_ISO8601, 8),
57+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Data!$A$2:$A$9', Properties::FORMAT_CODE_DATE_ISO8601, 8),
58+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Data!$A$2:$A$9', Properties::FORMAT_CODE_DATE_ISO8601, 8),
59+
];
60+
// Set the Data values for each data series we want to plot
61+
// Datatype
62+
// Cell reference for data
63+
// Format Code
64+
// Number of datapoints in series
65+
// Data values
66+
// Data Marker
67+
// Data Marker Color fill/[fill,Border]
68+
// Data Marker size
69+
// Color(s) added
70+
// added FORMAT_CODE_NUMBER
71+
$dataSeriesValues = [
72+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Data!$B$2:$B$9', Properties::FORMAT_CODE_NUMBER, 8, null, 'diamond', null, 5),
73+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Data!$C$2:$C$9', Properties::FORMAT_CODE_NUMBER, 8, null, 'square', '*accent1', 6),
74+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Data!$D$2:$D$9', Properties::FORMAT_CODE_NUMBER, 8, null, null, null, 7), // let Excel choose marker shape
75+
];
76+
// series 1 - metric1
77+
// marker details
78+
$dataSeriesValues[0]
79+
->getMarkerFillColor()
80+
->setColorProperties('0070C0', null, ChartColor::EXCEL_COLOR_TYPE_ARGB);
81+
$dataSeriesValues[0]
82+
->getMarkerBorderColor()
83+
->setColorProperties('002060', null, ChartColor::EXCEL_COLOR_TYPE_ARGB);
84+
85+
// line details - dashed, smooth line (Bezier) with arrows, 40% transparent
86+
$dataSeriesValues[0]
87+
->setSmoothLine(true)
88+
->setScatterLines(true)
89+
->setLineColorProperties('accent1', 40, ChartColor::EXCEL_COLOR_TYPE_SCHEME); // value, alpha, type
90+
$dataSeriesValues[0]->setLineStyleProperties(
91+
2.5, // width in points
92+
Properties::LINE_STYLE_COMPOUND_TRIPLE, // compound
93+
Properties::LINE_STYLE_DASH_SQUARE_DOT, // dash
94+
Properties::LINE_STYLE_CAP_SQUARE, // cap
95+
Properties::LINE_STYLE_JOIN_MITER, // join
96+
Properties::LINE_STYLE_ARROW_TYPE_OPEN, // head type
97+
Properties::LINE_STYLE_ARROW_SIZE_4, // head size preset index
98+
Properties::LINE_STYLE_ARROW_TYPE_ARROW, // end type
99+
Properties::LINE_STYLE_ARROW_SIZE_6 // end size preset index
100+
);
101+
102+
// series 2 - metric2, straight line - no special effects, connected
103+
$dataSeriesValues[1] // square marker border color
104+
->getMarkerBorderColor()
105+
->setColorProperties('accent6', 3, ChartColor::EXCEL_COLOR_TYPE_SCHEME);
106+
$dataSeriesValues[1] // square marker fill color
107+
->getMarkerFillColor()
108+
->setColorProperties('0FFF00', null, ChartColor::EXCEL_COLOR_TYPE_ARGB);
109+
$dataSeriesValues[1]
110+
->setScatterLines(true)
111+
->setSmoothLine(false)
112+
->setLineColorProperties('FF0000', 80, ChartColor::EXCEL_COLOR_TYPE_ARGB);
113+
$dataSeriesValues[1]->setLineWidth(2.0);
114+
115+
// series 3 - metric3, markers, no line
116+
$dataSeriesValues[2] // triangle? fill
117+
//->setPointMarker('triangle') // let Excel choose shape, which is predicted to be a triangle
118+
->getMarkerFillColor()
119+
->setColorProperties('FFFF00', null, ChartColor::EXCEL_COLOR_TYPE_ARGB);
120+
$dataSeriesValues[2] // triangle border
121+
->getMarkerBorderColor()
122+
->setColorProperties('accent4', null, ChartColor::EXCEL_COLOR_TYPE_SCHEME);
123+
$dataSeriesValues[2]->setScatterLines(false); // points not connected
124+
// Added so that Xaxis shows dates instead of Excel-equivalent-year1900-numbers
125+
$xAxis = new Axis();
126+
$xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_DATE_ISO8601, true);
127+
128+
// Build the dataseries
129+
$series = new DataSeries(
130+
DataSeries::TYPE_SCATTERCHART, // plotType
131+
null, // plotGrouping (Scatter charts don't have grouping)
132+
range(0, count($dataSeriesValues) - 1), // plotOrder
133+
$dataSeriesLabels, // plotLabel
134+
$xAxisTickValues, // plotCategory
135+
$dataSeriesValues, // plotValues
136+
null, // plotDirection
137+
null, // smooth line
138+
DataSeries::STYLE_SMOOTHMARKER // plotStyle
139+
);
140+
141+
// Set the series in the plot area
142+
$plotArea = new PlotArea(null, [$series]);
143+
// Set the chart legend
144+
$legend = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false);
145+
146+
$title = new Title('Test Scatter Chart');
147+
$yAxisLabel = new Title('Value ($k)');
148+
149+
// Create the chart
150+
$chart = new Chart(
151+
'chart1', // name
152+
$title, // title
153+
$legend, // legend
154+
$plotArea, // plotArea
155+
true, // plotVisibleOnly
156+
DataSeries::EMPTY_AS_GAP, // displayBlanksAs
157+
null, // xAxisLabel
158+
$yAxisLabel, // yAxisLabel
159+
// added xAxis for correct date display
160+
$xAxis, // xAxis
161+
// $yAxis, // yAxis
162+
);
163+
164+
// Set the position of the chart in the chart sheet
165+
$chart->setTopLeftPosition('A1');
166+
$chart->setBottomRightPosition('P12');
167+
168+
// create a 'Chart' worksheet, add $chart to it
169+
$spreadsheet->createSheet();
170+
$chartSheet = $spreadsheet->getSheet(1);
171+
$chartSheet->setTitle('Scatter Chart');
172+
173+
$chartSheet = $spreadsheet->getSheetByName('Scatter Chart');
174+
// Add the chart to the worksheet
175+
$chartSheet->addChart($chart);
176+
177+
// ------------ Demonstrate Trendlines for metric3 values in a new chart ------------
178+
179+
$dataSeriesLabels = [
180+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Data!$D$1', null, 1),
181+
];
182+
$xAxisTickValues = [
183+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Data!$A$2:$A$9', Properties::FORMAT_CODE_DATE_ISO8601, 8),
184+
];
185+
186+
$dataSeriesValues = [
187+
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Data!$D$2:$D$9', Properties::FORMAT_CODE_NUMBER, 4, null, 'triangle', null, 7),
188+
];
189+
190+
// add 3 trendlines:
191+
// 1- linear, double-ended arrow, w=0.5, same color as marker fill; nodispRSqr, nodispEq
192+
// 2- polynomial (order=3) no-arrow trendline, w=1.25, same color as marker fill; dispRSqr, dispEq
193+
// 3- moving Avg (period=2) single-arrow trendline, w=1.5, same color as marker fill; no dispRSqr, no dispEq
194+
$trendLines = [
195+
new TrendLine(TrendLine::TRENDLINE_LINEAR, null, null, false, false),
196+
new TrendLine(TrendLine::TRENDLINE_POLYNOMIAL, 3, null, true, true),
197+
new TrendLine(TrendLine::TRENDLINE_MOVING_AVG, null, 2, true),
198+
];
199+
$dataSeriesValues[0]->setTrendLines($trendLines);
200+
201+
// Suppress connecting lines; instead, add different Trendline algorithms to
202+
// determine how well the data fits the algorithm (Rsquared="goodness of fit")
203+
// Display RSqr plus the eqn just because we can.
204+
205+
$dataSeriesValues[0]->setScatterLines(false); // points not connected
206+
$dataSeriesValues[0]->getMarkerFillColor()
207+
->setColorProperties('FFFF00', null, ChartColor::EXCEL_COLOR_TYPE_ARGB);
208+
$dataSeriesValues[0]->getMarkerBorderColor()
209+
->setColorProperties('accent4', null, ChartColor::EXCEL_COLOR_TYPE_SCHEME);
210+
211+
// add properties to the trendLines - give each a different color
212+
$dataSeriesValues[0]->getTrendLines()[0]->getLineColor()->setColorProperties('accent4', null, ChartColor::EXCEL_COLOR_TYPE_SCHEME);
213+
$dataSeriesValues[0]->getTrendLines()[0]->setLineStyleProperties(0.5, null, null, null, null, Properties::LINE_STYLE_ARROW_TYPE_STEALTH, 5, Properties::LINE_STYLE_ARROW_TYPE_OPEN, 8);
214+
215+
$dataSeriesValues[0]->getTrendLines()[1]->getLineColor()->setColorProperties('accent3', null, ChartColor::EXCEL_COLOR_TYPE_SCHEME);
216+
$dataSeriesValues[0]->getTrendLines()[1]->setLineStyleProperties(1.25);
217+
218+
$dataSeriesValues[0]->getTrendLines()[2]->getLineColor()->setColorProperties('accent2', null, ChartColor::EXCEL_COLOR_TYPE_SCHEME);
219+
$dataSeriesValues[0]->getTrendLines()[2]->setLineStyleProperties(1.5, null, null, null, null, null, null, Properties::LINE_STYLE_ARROW_TYPE_OPEN, 8);
220+
221+
$xAxis = new Axis();
222+
$xAxis->setAxisNumberProperties(Properties::FORMAT_CODE_DATE_ISO8601); // m/d/yyyy
223+
224+
// Build the dataseries
225+
$series = new DataSeries(
226+
DataSeries::TYPE_SCATTERCHART, // plotType
227+
null, // plotGrouping (Scatter charts don't have grouping)
228+
range(0, count($dataSeriesValues) - 1), // plotOrder
229+
$dataSeriesLabels, // plotLabel
230+
$xAxisTickValues, // plotCategory
231+
$dataSeriesValues, // plotValues
232+
null, // plotDirection
233+
null, // smooth line
234+
DataSeries::STYLE_SMOOTHMARKER // plotStyle
235+
);
236+
237+
// Set the series in the plot area
238+
$plotArea = new PlotArea(null, [$series]);
239+
// Set the chart legend
240+
$legend = new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false);
241+
242+
$title = new Title('Test Scatter Chart - trendlines for metric3 values');
243+
$yAxisLabel = new Title('Value ($k)');
244+
245+
// Create the chart
246+
$chart = new Chart(
247+
'chart2', // name
248+
$title, // title
249+
$legend, // legend
250+
$plotArea, // plotArea
251+
true, // plotVisibleOnly
252+
DataSeries::EMPTY_AS_GAP, // displayBlanksAs
253+
null, // xAxisLabel
254+
$yAxisLabel, // yAxisLabel
255+
// added xAxis for correct date display
256+
$xAxis, // xAxis
257+
// $yAxis, // yAxis
258+
);
259+
260+
// Set the position of the chart in the chart sheet below the first chart
261+
$chart->setTopLeftPosition('A13');
262+
$chart->setBottomRightPosition('P25');
263+
264+
// Add the chart to the worksheet $chartSheet
265+
$chartSheet->addChart($chart);
266+
267+
// Save Excel 2007 file
268+
$filename = $helper->getFilename(__FILE__);
269+
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
270+
$writer->setIncludeCharts(true);
271+
$callStartTime = microtime(true);
272+
$writer->save($filename);
273+
$helper->logWrite($writer, $filename, $callStartTime);
1.03 KB
Binary file not shown.
Binary file not shown.

src/PhpSpreadsheet/Chart/Chart.php

+15
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ class Chart
147147
/** @var bool */
148148
private $noFill = false;
149149

150+
/** @var bool */
151+
private $roundedCorners = false;
152+
150153
/**
151154
* Create a new Chart.
152155
* majorGridlines and minorGridlines are deprecated, moved to Axis.
@@ -762,4 +765,16 @@ public function setNoFill(bool $noFill): self
762765

763766
return $this;
764767
}
768+
769+
public function getRoundedCorners(): bool
770+
{
771+
return $this->roundedCorners;
772+
}
773+
774+
public function setRoundedCorners(bool $roundedCorners): self
775+
{
776+
$this->roundedCorners = $roundedCorners;
777+
778+
return $this;
779+
}
765780
}

src/PhpSpreadsheet/Chart/DataSeriesValues.php

+15
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ class DataSeriesValues extends Properties
8888
/** @var ?Layout */
8989
private $labelLayout;
9090

91+
/** @var TrendLine[] */
92+
private $trendLines = [];
93+
9194
/**
9295
* Create a new DataSeriesValues object.
9396
*
@@ -577,4 +580,16 @@ public function setLabelLayout(?Layout $labelLayout): self
577580

578581
return $this;
579582
}
583+
584+
public function setTrendLines(array $trendLines): self
585+
{
586+
$this->trendLines = $trendLines;
587+
588+
return $this;
589+
}
590+
591+
public function getTrendLines(): array
592+
{
593+
return $this->trendLines;
594+
}
580595
}

0 commit comments

Comments
 (0)