AutorÃa | Ultima modificación | Ver Log |
<?php
namespace PhpOffice\PhpSpreadsheet\Chart;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class DataSeriesValues extends Properties
{
const DATASERIES_TYPE_STRING = 'String';
const DATASERIES_TYPE_NUMBER = 'Number';
private const DATA_TYPE_VALUES = [
self::DATASERIES_TYPE_STRING,
self::DATASERIES_TYPE_NUMBER,
];
/**
* Series Data Type.
*/
private string $dataType;
/**
* Series Data Source.
*/
private ?string $dataSource;
/**
* Format Code.
*/
private ?string $formatCode;
/**
* Series Point Marker.
*/
private ?string $pointMarker;
private ChartColor $markerFillColor;
private ChartColor $markerBorderColor;
/**
* Series Point Size.
*/
private int $pointSize = 3;
/**
* Point Count (The number of datapoints in the dataseries).
*/
private int $pointCount;
/**
* Data Values.
*/
private ?array $dataValues;
/**
* Fill color (can be array with colors if dataseries have custom colors).
*
* @var null|ChartColor|ChartColor[]
*/
private $fillColor;
private bool $scatterLines = true;
private bool $bubble3D = false;
private ?Layout $labelLayout = null;
/** @var TrendLine[] */
private array $trendLines = [];
/**
* Create a new DataSeriesValues object.
*
* @param null|ChartColor|ChartColor[]|string|string[] $fillColor
*/
public function __construct(
string $dataType = self::DATASERIES_TYPE_NUMBER,
?string $dataSource = null,
?string $formatCode = null,
int $pointCount = 0,
?array $dataValues = [],
?string $marker = null,
null|ChartColor|array|string $fillColor = null,
int|string $pointSize = 3
) {
parent::__construct();
$this->markerFillColor = new ChartColor();
$this->markerBorderColor = new ChartColor();
$this->setDataType($dataType);
$this->dataSource = $dataSource;
$this->formatCode = $formatCode;
$this->pointCount = $pointCount;
$this->dataValues = $dataValues;
$this->pointMarker = $marker;
if ($fillColor !== null) {
$this->setFillColor($fillColor);
}
if (is_numeric($pointSize)) {
$this->pointSize = (int) $pointSize;
}
}
/**
* Get Series Data Type.
*/
public function getDataType(): string
{
return $this->dataType;
}
/**
* Set Series Data Type.
*
* @param string $dataType Datatype of this data series
* Typical values are:
* DataSeriesValues::DATASERIES_TYPE_STRING
* Normally used for axis point values
* DataSeriesValues::DATASERIES_TYPE_NUMBER
* Normally used for chart data values
*
* @return $this
*/
public function setDataType(string $dataType): static
{
if (!in_array($dataType, self::DATA_TYPE_VALUES)) {
throw new Exception('Invalid datatype for chart data series values');
}
$this->dataType = $dataType;
return $this;
}
/**
* Get Series Data Source (formula).
*/
public function getDataSource(): ?string
{
return $this->dataSource;
}
/**
* Set Series Data Source (formula).
*
* @return $this
*/
public function setDataSource(?string $dataSource): static
{
$this->dataSource = $dataSource;
return $this;
}
/**
* Get Point Marker.
*/
public function getPointMarker(): ?string
{
return $this->pointMarker;
}
/**
* Set Point Marker.
*
* @return $this
*/
public function setPointMarker(string $marker): static
{
$this->pointMarker = $marker;
return $this;
}
public function getMarkerFillColor(): ChartColor
{
return $this->markerFillColor;
}
public function getMarkerBorderColor(): ChartColor
{
return $this->markerBorderColor;
}
/**
* Get Point Size.
*/
public function getPointSize(): int
{
return $this->pointSize;
}
/**
* Set Point Size.
*
* @return $this
*/
public function setPointSize(int $size = 3): static
{
$this->pointSize = $size;
return $this;
}
/**
* Get Series Format Code.
*/
public function getFormatCode(): ?string
{
return $this->formatCode;
}
/**
* Set Series Format Code.
*
* @return $this
*/
public function setFormatCode(string $formatCode): static
{
$this->formatCode = $formatCode;
return $this;
}
/**
* Get Series Point Count.
*/
public function getPointCount(): int
{
return $this->pointCount;
}
/**
* Get fill color object.
*
* @return null|ChartColor|ChartColor[]
*/
public function getFillColorObject()
{
return $this->fillColor;
}
private function stringToChartColor(string $fillString): ChartColor
{
$value = $type = '';
if (str_starts_with($fillString, '*')) {
$type = 'schemeClr';
$value = substr($fillString, 1);
} elseif (str_starts_with($fillString, '/')) {
$type = 'prstClr';
$value = substr($fillString, 1);
} elseif ($fillString !== '') {
$type = 'srgbClr';
$value = $fillString;
$this->validateColor($value);
}
return new ChartColor($value, null, $type);
}
private function chartColorToString(ChartColor $chartColor): string
{
$type = (string) $chartColor->getColorProperty('type');
$value = (string) $chartColor->getColorProperty('value');
if ($type === '' || $value === '') {
return '';
}
if ($type === 'schemeClr') {
return "*$value";
}
if ($type === 'prstClr') {
return "/$value";
}
return $value;
}
/**
* Get fill color.
*
* @return string|string[] HEX color or array with HEX colors
*/
public function getFillColor(): string|array
{
if ($this->fillColor === null) {
return '';
}
if (is_array($this->fillColor)) {
$array = [];
foreach ($this->fillColor as $chartColor) {
$array[] = $this->chartColorToString($chartColor);
}
return $array;
}
return $this->chartColorToString($this->fillColor);
}
/**
* Set fill color for series.
*
* @param ChartColor|ChartColor[]|string|string[] $color HEX color or array with HEX colors
*
* @return $this
*/
public function setFillColor($color): static
{
if (is_array($color)) {
$this->fillColor = [];
foreach ($color as $fillString) {
if ($fillString instanceof ChartColor) {
$this->fillColor[] = $fillString;
} else {
$this->fillColor[] = $this->stringToChartColor($fillString);
}
}
} elseif ($color instanceof ChartColor) {
$this->fillColor = $color;
} else {
$this->fillColor = $this->stringToChartColor($color);
}
return $this;
}
/**
* Method for validating hex color.
*
* @param string $color value for color
*
* @return bool true if validation was successful
*/
private function validateColor(string $color): bool
{
if (!preg_match('/^[a-f0-9]{6}$/i', $color)) {
throw new Exception(sprintf('Invalid hex color for chart series (color: "%s")', $color));
}
return true;
}
/**
* Get line width for series.
*/
public function getLineWidth(): null|float|int
{
return $this->lineStyleProperties['width'];
}
/**
* Set line width for the series.
*
* @return $this
*/
public function setLineWidth(null|float|int $width): static
{
$this->lineStyleProperties['width'] = $width;
return $this;
}
/**
* Identify if the Data Series is a multi-level or a simple series.
*/
public function isMultiLevelSeries(): ?bool
{
if (!empty($this->dataValues)) {
return is_array(array_values($this->dataValues)[0]);
}
return null;
}
/**
* Return the level count of a multi-level Data Series.
*/
public function multiLevelCount(): int
{
$levelCount = 0;
foreach (($this->dataValues ?? []) as $dataValueSet) {
$levelCount = max($levelCount, count($dataValueSet));
}
return $levelCount;
}
/**
* Get Series Data Values.
*/
public function getDataValues(): ?array
{
return $this->dataValues;
}
/**
* Get the first Series Data value.
*/
public function getDataValue(): mixed
{
if ($this->dataValues === null) {
return null;
}
$count = count($this->dataValues);
if ($count == 0) {
return null;
} elseif ($count == 1) {
return $this->dataValues[0];
}
return $this->dataValues;
}
/**
* Set Series Data Values.
*
* @return $this
*/
public function setDataValues(array $dataValues): static
{
$this->dataValues = Functions::flattenArray($dataValues);
$this->pointCount = count($dataValues);
return $this;
}
public function refresh(Worksheet $worksheet, bool $flatten = true): void
{
if ($this->dataSource !== null) {
$calcEngine = Calculation::getInstance($worksheet->getParent());
$newDataValues = Calculation::unwrapResult(
$calcEngine->_calculateFormulaValue(
'=' . $this->dataSource,
null,
$worksheet->getCell('A1')
)
);
if ($flatten) {
$this->dataValues = Functions::flattenArray($newDataValues);
foreach ($this->dataValues as &$dataValue) {
if (is_string($dataValue) && !empty($dataValue) && $dataValue[0] == '#') {
$dataValue = 0.0;
}
}
unset($dataValue);
} else {
[, $cellRange] = Worksheet::extractSheetTitle($this->dataSource, true);
$dimensions = Coordinate::rangeDimension(str_replace('$', '', $cellRange ?? ''));
if (($dimensions[0] == 1) || ($dimensions[1] == 1)) {
$this->dataValues = Functions::flattenArray($newDataValues);
} else {
/** @var array<int, array> */
$newDataValuesx = $newDataValues;
$newArray = array_values(array_shift($newDataValuesx) ?? []);
foreach ($newArray as $i => $newDataSet) {
$newArray[$i] = [$newDataSet];
}
foreach ($newDataValuesx as $newDataSet) {
$i = 0;
foreach ($newDataSet as $newDataVal) {
array_unshift($newArray[$i++], $newDataVal);
}
}
$this->dataValues = $newArray;
}
}
$this->pointCount = count($this->dataValues);
}
}
public function getScatterLines(): bool
{
return $this->scatterLines;
}
public function setScatterLines(bool $scatterLines): self
{
$this->scatterLines = $scatterLines;
return $this;
}
public function getBubble3D(): bool
{
return $this->bubble3D;
}
public function setBubble3D(bool $bubble3D): self
{
$this->bubble3D = $bubble3D;
return $this;
}
/**
* Smooth Line. Must be specified for both DataSeries and DataSeriesValues.
*/
private bool $smoothLine = false;
/**
* Get Smooth Line.
*/
public function getSmoothLine(): bool
{
return $this->smoothLine;
}
/**
* Set Smooth Line.
*
* @return $this
*/
public function setSmoothLine(bool $smoothLine): static
{
$this->smoothLine = $smoothLine;
return $this;
}
public function getLabelLayout(): ?Layout
{
return $this->labelLayout;
}
public function setLabelLayout(?Layout $labelLayout): self
{
$this->labelLayout = $labelLayout;
return $this;
}
public function setTrendLines(array $trendLines): self
{
$this->trendLines = $trendLines;
return $this;
}
public function getTrendLines(): array
{
return $this->trendLines;
}
/**
* Implement PHP __clone to create a deep clone, not just a shallow copy.
*/
public function __clone()
{
parent::__clone();
$this->markerFillColor = clone $this->markerFillColor;
$this->markerBorderColor = clone $this->markerBorderColor;
if (is_array($this->fillColor)) {
$fillColor = $this->fillColor;
$this->fillColor = [];
foreach ($fillColor as $color) {
$this->fillColor[] = clone $color;
}
} elseif ($this->fillColor instanceof ChartColor) {
$this->fillColor = clone $this->fillColor;
}
$this->labelLayout = ($this->labelLayout === null) ? null : clone $this->labelLayout;
$trendLines = $this->trendLines;
$this->trendLines = [];
foreach ($trendLines as $trendLine) {
$this->trendLines[] = clone $trendLine;
}
}
}