Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Date Axis in Line Charts #2967

Closed
2 of 8 tasks
bridgeplayr opened this issue Jul 29, 2022 · 1 comment · Fixed by #3018
Closed
2 of 8 tasks

Add support for Date Axis in Line Charts #2967

bridgeplayr opened this issue Jul 29, 2022 · 1 comment · Fixed by #3018
Labels

Comments

@bridgeplayr
Copy link

This is:

- [ ] a bug report
- [x] a feature request
- [ ] **not** a usage question (ask them on https://stackoverflow.com/questions/tagged/phpspreadsheet or https://gitter.im/PHPOffice/PhpSpreadsheet)

What is the expected behavior?

What is the current behavior?

Supported in Excel; not available in PHPss

What are the steps to reproduce?

Please provide a Minimal, Complete, and Verifiable example of code that exhibits the issue without relying on an external Excel file or a web server:

<?php

require __DIR__ . '/vendor/autoload.php';

// add code that show the issue here...

If this is an issue with reading a specific spreadsheet file, then it may be appropriate to provide a sample file that demonstrates the problem; but please keep it as small as possible, and sanitize any confidential information before uploading.

What features do you think are causing the issue

  • Reader
  • Writer
  • Styles
  • Data Validations
  • Formula Calculations
  • Charts
  • AutoFilter
  • Form Elements

Does an issue affect all spreadsheet file formats? If not, which formats are affected?

Xlsx

Which versions of PhpSpreadsheet and PHP are affected?

1.23 7.3

@bridgeplayr
Copy link
Author

bridgeplayr commented Jul 29, 2022

Sample spreadsheet creator is derived from samples/Chart/33_Chart_create_scatter.php
33_LineChart_DateAxis.php.txt

  • Data remains in worksheet "Data" with 8 datapoints instead of 4
  • some dates changed to not end on quarter boundaries
  • date format changed to mm/dd/yyyy, to show that PhpSpreadsheet\Calculation\DateTimeExcel does not correctly calculate the date number
  • Charts are moved to a separate worksheet
  • 2 Charts are created in worksheet "Scatter+Line Chart"
    • ScatterChart showing dates but no tickmarks
    • LineChart showing dates with tickmarks determined by user

Additional functions are required:

function DateRange ($nrows, $Wrkbk) {
  $Datasheet = $Wrkbk->getSheetByName('Data');

  // start the xaxis at the beginning of the quarter of the first date
  $startDateStr = $Datasheet->getCell('B2')->getValue(); // mm/dd/yyyy date string
  $startDate    = date_create_from_format('m/d/Y', $startDateStr); // php date obj

  // get date of first day of the quarter of the start date
  $startMonth   = date_format($startDate ,'n'); // suppress leading zero
  $startYr      = date_format($startDate ,'Y'); 
  $qtr          = intdiv($startMonth, 3)+ (($startMonth % 3 > 0) ? 1:0);
  $qtrStartMonth= 1+ (($qtr-1) * 3);
  $qtrStartStr  = "$qtrStartMonth/1/$startYr";
  $ExcelQtrStartDateVal = xlDateValue($qtrStartStr); // derived from pure php code

  // end the xaxis at the end of the quarter of the last date
  $lastDateStr   = $Datasheet->getCellByColumnAndRow(2, $nrows+1)->getValue();
  $lastDate      = date_create_from_format('m/d/Y', $lastDateStr); 
  $lastMonth     = date_format($lastDate, 'n'); 
  $lastYr        = date_format($lastDate, 'Y'); 
  $qtr          = intdiv($lastMonth, 3) + (($lastMonth % 3 > 0) ? 1:0);
  $qtrEndMonth  = 3+ (($qtr-1) * 3);
  $lastDOM      = cal_days_in_month(CAL_GREGORIAN, $qtrEndMonth,$lastYr);
  $qtrEndStr    = "$qtrEndMonth/$lastDOM/$lastYr";
  $ExcelQtrEndDateVal = xlDateValue($qtrEndStr); // derived from pure php code

  $minMaxDates = ['min' => $ExcelQtrStartDateVal, 'max' => $ExcelQtrEndDateVal ];
  return $minMaxDates;
}

function xlDateValue($datestr) {
  // must invent this function since phpSpreadsheet's DATEVALUE implementation 
  // insists that 1/10/2020 is Oct 1 instead of Jan 10, proud that it ignores 
  // any date formatting that the user might impose on the date string.
  $dayOne       = date_create_from_format('m/d/Y', "1/1/1900");
  $dateObj      = date_create_from_format('m/d/Y', $datestr); 
  $daysAfterOne = date_diff($dayOne, $dateObj);
  $excelDateVal = $daysAfterOne->format("%a");
  // no explanation for requirement to add 2; just accept it
  return 2+$excelDateVal; // according to php as opposed to phpss
}
  • Add to Chart/Properties.php
    const
        TIME_UNIT_DAYS = 'days';
    const TIME_UNIT_MONTHS = 'months';
    const TIME_UNIT_YEARS = 'years';
  • Modify Chart/Axis.php - add majorTimeUnit, minorTimeUnit, baseTimeUnit, and auto

    private $axisOptions = [
    'minimum' => null,
    'maximum' => null,
    'majorTimeUnit' => self::TIME_UNIT_YEARS,
    'minorTimeUnit' => self::TIME_UNIT_MONTHS,
    'baseTimeUnit' => self::TIME_UNIT_DAYS,
    'major_unit' => null,
    'minor_unit' => null,
    'orientation' => self::ORIENTATION_NORMAL,
    'minor_tick_mark' => self::TICK_MARK_NONE,
    'major_tick_mark' => self::TICK_MARK_NONE,
    'axis_labels' => self::AXIS_LABELS_NEXT_TO,
    'horizontal_crosses' => self::HORIZONTAL_CROSSES_AUTOZERO,
    'horizontal_crosses_value' => null,
    'auto' => null,
    ];

    /**

    • Set Axis Options Properties.
      */
      public function setAxisOptionsProperties(
      string $axisLabels,
      ?string $horizontalCrossesValue = null,
      ?string $horizontalCrosses = null,
      ?string $axisOrientation = null,
      ?string $majorTmt = null,
      ?string $minorTmt = null,
      ?string $minimum = null,
      ?string $maximum = null,
      ?string $majorUnit = null,
      ?string $minorUnit = null,
      ?string $majorTimeUnit = null,
      ?string $minorTimeUnit = null,
      ?string $baseTimeUnit = null,
      ?string $auto = '1'
      ): void {
      $this->axisOptions['axis_labels'] = $axisLabels;
      $this->setAxisOption('horizontal_crosses_value', $horizontalCrossesValue);
      $this->setAxisOption('horizontal_crosses', $horizontalCrosses);
      $this->setAxisOption('orientation', $axisOrientation);
      $this->setAxisOption('major_tick_mark', $majorTmt);
      $this->setAxisOption('minor_tick_mark', $minorTmt);
      $this->setAxisOption('minimum', $minimum);
      $this->setAxisOption('maximum', $maximum);
      $this->setAxisOption('major_unit', $majorUnit);
      $this->setAxisOption('minor_unit', $minorUnit);
      $this->setAxisOption('majorTimeUnit', $majorTimeUnit);
      $this->setAxisOption('minorTimeUnit', $minorTimeUnit);
      $this->setAxisOption('baseTimeUnit', $baseTimeUnit);
      $this->setAxisOption('auto', $auto); // allow Excel to determine axis type
      }
  • Add 'dateAx' -- LineCharts only; not available in ScatterCharts


    public function setAxisType(string $type): self
    {
        if ($type === 'catAx' || $type === 'valAx' || $type === 'dateAx') {
            $this->axisType = $type;
        } else {
            $this->axisType = '';
        }

        return $this;
    }

  • modify Writer/Xlsx/Chart.php

  • modify writeCategoryAxis()
    
    private function writeCategoryAxis(XMLWriter $objWriter, ?Title $xAxisLabel, $id1, $id2, $isMultiLevelSeries, Axis $yAxis, GridLines $majorGridlines, GridLines $minorGridlines): void
    {
        // N.B. writeCategoryAxis may be invoked with $xAxis substituted for the last parameter($yAxis) for ScatterChart, etc
        // In that case, xAxis may contain values like the yAxis, or it may be a date axis (LINECHART).
        $AxisType = $yAxis->getAxisType() ;
        if ($AxisType !== '') {
            $objWriter->startElement('c:' . $AxisType);
        } elseif ($yAxis->getAxisIsNumericFormat()) {
            $objWriter->startElement('c:valAx');
        } else {
            $objWriter->startElement('c:catAx');
        }
  • below line $objWriter->startElement('c:auto'); -- modify value of 'auto'
        // $objWriter->writeAttribute('val', 1); // LineChart with dateAx wants '0', but we don't pass in $Chart to test; could test for axisType == 'dateAx'
`        $objWriter->writeAttribute('val', (int) $yAxis->getAxisOptionsProperty('auto'));`
        $objWriter->endElement();

        $objWriter->startElement('c:lblAlgn');
        $objWriter->writeAttribute('val', 'ctr');
        $objWriter->endElement();

        $objWriter->startElement('c:lblOffset');
        $objWriter->writeAttribute('val', 100);
        $objWriter->endElement();

        if (!empty($yAxis->getAxisOptionsProperty('baseTimeUnit')) && $AxisType == 'dateAx') {
            $objWriter->startElement('c:baseTimeUnit');
            $objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('baseTimeUnit')) ;
            $objWriter->endElement();
        }

        if (!empty($yAxis->getAxisOptionsProperty('majorTimeUnit')) && $AxisType == 'dateAx') {
            $objWriter->startElement('c:majorTimeUnit');
            $objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('majorTimeUnit')) ;
            $objWriter->endElement();
        }

        if (!empty($yAxis->getAxisOptionsProperty('minorbaseTimeUnit')) && $AxisType == 'dateAx') {
            $objWriter->startElement('c:minorTimeUnit');
            $objWriter->writeAttribute('val', $yAxis->getAxisOptionsProperty('minorTimeUnit')) ;
            $objWriter->endElement();
        }

        if ($isMultiLevelSeries) {
            $objWriter->startElement('c:noMultiLvlLbl');
            $objWriter->writeAttribute('val', 0);
            $objWriter->endElement();
        }
        $objWriter->endElement(); // c:$AxisType
    }
  • modify method writePlotGroup()
  • add TYPE_LINECHART qualifier to $nofill assignment

$nofill = $groupType == DataSeries::TYPE_LINECHART || $groupType == DataSeries::TYPE_STOCKCHART || ($groupType === DataSeries::TYPE_SCATTERCHART && !$plotSeriesValues->getScatterLines());
33_LineChart_DateAxis.xlsx

oleibman added a commit to oleibman/PhpSpreadsheet that referenced this issue Aug 20, 2022
Fix PHPOffice#2967. Fix PHPOffice#2969 (which had already been fixed prior to opening the issue, but had added urgency for Date Axes). Add ability to set axis type to date axis, in addition to original possiblities of value axis and category axis.
oleibman added a commit that referenced this issue Aug 25, 2022
* Charts - Add Support for Date Axis

Fix #2967. Fix #2969 (which had already been fixed prior to opening the issue, but had added urgency for Date Axes). Add ability to set axis type to date axis, in addition to original possiblities of value axis and category axis.

* Update 33_Chart_create_line_dateaxis.php

No idea why php-cs-fixer is complaining. It didn't do so when I first uploaded. I can't duplicate problem on my own system. Not enough detail in error message for me to act. Grasping at straws, I have moved the function definition (which is the only use of braces in the entire script) from the end of the script to the beginning.

* Update 33_Chart_create_line_dateaxis.php

Some comments were mis-aligned. This may be related to the reasons behind PR #3025, which didn't take care of this because this script had not yet been merged.
MarkBaker added a commit that referenced this issue Sep 25, 2022
### Added

- Implementation of the new `TEXTBEFORE()`, `TEXTAFTER()` and `TEXTSPLIT()` Excel Functions
- Implementation of the `ARRAYTOTEXT()` and `VALUETOTEXT()` Excel Functions
- Support for [mitoteam/jpgraph](https://packagist.org/packages/mitoteam/jpgraph) implementation of
  JpGraph library to render charts added.
- Charts: Add Gradients, Transparency, Hidden Axes, Rounded Corners, Trendlines, Date Axes.

### Changed

- Allow variant behaviour when merging cells [Issue #3065](#3065)
  - Merge methods now allow an additional `$behaviour` argument. Permitted values are:
    - Worksheet::MERGE_CELL_CONTENT_EMPTY - Empty the content of the hidden cells (the default behaviour)
    - Worksheet::MERGE_CELL_CONTENT_HIDE - Keep the content of the hidden cells
    - Worksheet::MERGE_CELL_CONTENT_MERGE - Move the content of the hidden cells into the first cell

### Deprecated

- Axis getLineProperty deprecated in favor of getLineColorProperty.
- Moved majorGridlines and minorGridlines from Chart to Axis. Setting either in Chart constructor or through Chart methods, or getting either using Chart methods is deprecated.
- Chart::EXCEL_COLOR_TYPE_* copied from Properties to ChartColor; use in Properties is deprecated.
- ChartColor::EXCEL_COLOR_TYPE_ARGB deprecated in favor of EXCEL_COLOR_TYPE_RGB ("A" component was never allowed).
- Misspelled Properties::LINE_STYLE_DASH_SQUERE_DOT deprecated in favor of LINE_STYLE_DASH_SQUARE_DOT.
- Clone not permitted for Spreadsheet. Spreadsheet->copy() can be used instead.

### Removed

- Nothing

### Fixed

- Fix update to defined names when inserting/deleting rows/columns [Issue #3076](#3076) [PR #3077](#3077)
- Fix DataValidation sqRef when inserting/deleting rows/columns [Issue #3056](#3056) [PR #3074](#3074)
- Named ranges not usable as anchors in OFFSET function [Issue #3013](#3013)
- Fully flatten an array [Issue #2955](#2955) [PR #2956](#2956)
- cellExists() and getCell() methods should support UTF-8 named cells [Issue #2987](#2987) [PR #2988](#2988)
- Spreadsheet copy fixed, clone disabled. [PR #2951](#2951)
- Fix PDF problems with text rotation and paper size. [Issue #1747](#1747) [Issue #1713](#1713) [PR #2960](#2960)
- Limited support for chart titles as formulas [Issue #2965](#2965) [Issue #749](#749) [PR #2971](#2971)
- Add Gradients, Transparency, and Hidden Axes to Chart [Issue #2257](#2257) [Issue #2229](#2929) [Issue #2935](#2935) [PR #2950](#2950)
- Chart Support for Rounded Corners and Trendlines [Issue #2968](#2968) [Issue #2815](#2815) [PR #2976](#2976)
- Add setName Method for Chart [Issue #2991](#2991) [PR #3001](#3001)
- Eliminate partial dependency on php-intl in StringHelper [Issue #2982](#2982) [PR #2994](#2994)
- Minor changes for Pdf [Issue #2999](#2999) [PR #3002](#3002) [PR #3006](#3006)
- Html/Pdf Do net set background color for cells using (default) nofill [PR #3016](#3016)
- Add support for Date Axis to Chart [Issue #2967](#2967) [PR #3018](#3018)
- Reconcile Differences Between Css and Excel for Cell Alignment [PR #3048](#3048)
- R1C1 Format Internationalization and Better Support for Relative Offsets [Issue #1704](#1704) [PR #3052](#3052)
- Minor Fix for Percentage Formatting [Issue #1929](#1929) [PR #3053](#3053)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging a pull request may close this issue.

2 participants