Изменение диапазона данных в линейной диаграмме Excel с использованием Apache POI - PullRequest
2 голосов
/ 16 мая 2019

Я пытаюсь узнать, возможно ли изменить диапазон данных ряда в линейной диаграмме с помощью Apache POI.

Я могу вытащить ряд из самой диаграммы, но не могу найти метод, который позволяет мне изменять диапазон данных.

XSSFWorkbook workbook = new XSSFWorkbook("C:\\Workbook.xlsx");
Sheet worksheet = workbook.getSheetAt(0);
XSSFDrawing drawing = (XSSFDrawing) worksheet.createDrawingPatriarch();
List<XSSFChart> charts = drawing.getCharts();
for (XSSFChart chart : charts) {
    String title = chart.getTitleText().toString();
    if (title.equals("Z-Acceleration")) {
        CTChart cc = chart.getCTChart();
        CTPlotArea plotArea = cc.getPlotArea();
        CTLineSer[] ccc = plotArea.getLineChartArray()[0].getSerArray();
        for (CTLineSer s : ccc) {
            System.out.println(s.xmlText());
        }
        System.out.println(ccc.length);
    }
}

Я распечатал текст XML, чтобы увидеть, действительно ли он смог правильно вытащить ряд из диаграммы и смог найти его заголовок и диапазон данных, но не смог изменить его.

1 Ответ

2 голосов
/ 18 мая 2019

ОК, так как это вообще хороший вопрос, давайте приведем конкретный пример того, как изменить диапазон данных в линейной диаграмме Excel, используя apache poi.

Давайте начнем со следующего листа:

enter image description here

Тогда следующий код:

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.SpreadsheetVersion;

import org.openxmlformats.schemas.drawingml.x2006.chart.*;

import java.io.FileInputStream;
import java.io.FileOutputStream;

import java.util.List;

class ExcelChangeChartDataSource {

 static XSSFChart getChartWithTitle(XSSFSheet sheet, String wantedTitle) {
  if (sheet == null || wantedTitle == null) return null;
  XSSFDrawing drawing = sheet.createDrawingPatriarch();
  List<XSSFChart> charts = drawing.getCharts();
  for (XSSFChart chart : charts) {
   String title = chart.getTitleText().toString();
   if (wantedTitle.equals(title)) return chart;
  }
  return null;
 }

 static void addMonthDataToChart(XSSFSheet sheet, XSSFChart chart, String month, Double[] seriesData) {
  CTChart ctChart = chart.getCTChart();
  CTPlotArea ctPlotArea = ctChart.getPlotArea();
  List<CTLineSer> ctLineSerList = ctPlotArea.getLineChartArray(0).getSerList();

  Row row;
  Cell cell;
  int ser = 0;
  for (CTLineSer ctLineSer : ctLineSerList) {

   CTAxDataSource cttAxDataSource = ctLineSer.getCat();
   CTStrRef ctStrRef = cttAxDataSource.getStrRef();

   AreaReference catReference = new AreaReference(ctStrRef.getF(), SpreadsheetVersion.EXCEL2007);
   CellReference firstCatCell = catReference.getFirstCell();
   CellReference lastCatCell = catReference.getLastCell();
   if (firstCatCell.getCol() == lastCatCell.getCol()) {
    int col = firstCatCell.getCol();
    int lastRow = lastCatCell.getRow();
    row = sheet.getRow(lastRow+1); if (row == null) row = sheet.createRow(lastRow+1);
    cell = row.getCell(col); if (cell == null) cell = row.createCell(col);
    cell.setCellValue(month);

    ctStrRef.setF(new AreaReference(
                  firstCatCell, 
                  new CellReference(lastCatCell.getSheetName(), lastRow+1, col, true, true), 
                  SpreadsheetVersion.EXCEL2007).formatAsString()
                 );

    CTNumDataSource ctNumDataSource = ctLineSer.getVal();
    CTNumRef ctNumRef = ctNumDataSource.getNumRef();

    AreaReference numReference = new AreaReference(ctNumRef.getF(), SpreadsheetVersion.EXCEL2007);
    CellReference firstNumCell = numReference.getFirstCell();
    CellReference lastNumCell = numReference.getLastCell();
    if (lastNumCell.getRow() == lastRow && firstNumCell.getCol() == lastNumCell.getCol()) {
     col = firstNumCell.getCol();
     row = sheet.getRow(lastRow+1); if (row == null) row = sheet.createRow(lastRow+1);
     cell = row.getCell(col); if (cell == null) cell = row.createCell(col);
     if (ser < seriesData.length) cell.setCellValue(seriesData[ser]);

     ctNumRef.setF(new AreaReference(
                   firstNumCell, 
                   new CellReference(lastNumCell.getSheetName(), lastRow+1, col, true, true), 
                   SpreadsheetVersion.EXCEL2007).formatAsString()
                  );
    }
   }
   ser++;
  }
 }

 public static void main(String[] args) throws Exception {

  XSSFWorkbook workbook = (XSSFWorkbook)WorkbookFactory.create(new FileInputStream("WorkbookWithChart.xlsx"));

  XSSFSheet sheet = workbook.getSheetAt(0);

  XSSFChart chart = getChartWithTitle(sheet, "Z-Acceleration"); 

  if (chart != null) {
   addMonthDataToChart(sheet, chart, "Apr", new Double[]{7d,3d,5d});
   addMonthDataToChart(sheet, chart, "Mai", new Double[]{2d,6d,8d});
   addMonthDataToChart(sheet, chart, "Jun", new Double[]{1d,9d,4d});
   addMonthDataToChart(sheet, chart, "Jul", new Double[]{5d,6d});
  }

  FileOutputStream out = new FileOutputStream("WorkbookWithChartNew.xlsx");
  workbook.write(out);
  out.close();
  workbook.close();
 }
}

дает следующий результат:

enter image description here

Этот код использует классы org.openxmlformats.schemas.drawingml.x2006.chart.* и может использоваться с apache poi 3.17, а также с apache poi 4.1.0.

К сожалению, API документация org.openxmlformats.schemas.drawingml.x2006.chart.* не доступна для общественности,Поэтому, если нам это нужно, нам нужно загрузить ooxml-schemas-1.3-sources.jar с central.maven.org / maven2 / org / apache / poi / ooxml-schemas / 1.3 .Тогда распакуйте это.Затем перейдите в каталог ooxml-schemas-1.3 и выполните javadoc -d javadoc -sourcepath ./ -subpackages org.После этого мы находим API документы в ooxml-schemas-1.3/javadoc.Начните читать с overview-tree.html.

Для apache poi 4.1.0 нам нужно ooxml-schemas-1.4.

Я пробовал то же самое, используя новый материал XDDF в apache poi 4.1.0.Но сначала код на самом деле не намного дешевле, а во-вторых, он имеет тот недостаток, что XDDFChart.plot дает сбой, когда некоторые данные в XDDFNumericalDataSource<Double> values отсутствуют.Тогда мы должны установить эти точки данных 0. Но это не то же самое, что не присутствовать.Так что использование нового XDDF в этом случае на самом деле не прогресс.Но тем не менее, вот код, который я пробовал:

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.SpreadsheetVersion;

import org.apache.poi.xddf.usermodel.chart.*;

import java.io.FileInputStream;
import java.io.FileOutputStream;

import java.util.List;

class ExcelChangeChartDataSource {

 static XSSFChart getChartWithTitle(XSSFSheet sheet, String wantedTitle) {
  if (sheet == null || wantedTitle == null) return null;
  XSSFDrawing drawing = sheet.createDrawingPatriarch();
  List<XSSFChart> charts = drawing.getCharts();
  for (XSSFChart chart : charts) {
   String title = chart.getTitleText().toString();
   if (wantedTitle.equals(title)) return chart;
  }
  return null;
 }

 static void addMonthDataToChart(XSSFSheet sheet, XSSFChart chart, String month, Double[] seriesData) {
  Row row;
  Cell cell;

  List<XDDFChartData> chartDataList = chart.getChartSeries();
  XDDFChartData chartData = chartDataList.get(0);

  List<XDDFChartData.Series> seriesList = chartData.getSeries();
  int ser = 0;
  for (XDDFChartData.Series series : seriesList) {
   XDDFDataSource categoryData = series.getCategoryData();
   AreaReference catReference = new AreaReference(categoryData.getDataRangeReference(), SpreadsheetVersion.EXCEL2007);
   CellReference firstCatCell = catReference.getFirstCell();
   CellReference lastCatCell = catReference.getLastCell();
   if (firstCatCell.getCol() == lastCatCell.getCol()) {
    int col = firstCatCell.getCol();
    int lastRow = lastCatCell.getRow();
    row = sheet.getRow(lastRow+1); if (row == null) row = sheet.createRow(lastRow+1);
    cell = row.getCell(col); if (cell == null) cell = row.createCell(col);
    cell.setCellValue(month);

    XDDFDataSource<String> category = XDDFDataSourcesFactory.fromStringCellRange(
                                       sheet, 
                                       new CellRangeAddress(firstCatCell.getRow(), lastRow+1, col, col));

    XDDFNumericalDataSource valuesData = series.getValuesData();
    AreaReference numReference = new AreaReference(valuesData.getDataRangeReference(), SpreadsheetVersion.EXCEL2007);
    CellReference firstNumCell = numReference.getFirstCell();
    CellReference lastNumCell = numReference.getLastCell();
    if (lastNumCell.getRow() == lastRow && firstNumCell.getCol() == lastNumCell.getCol()) {
     col = firstNumCell.getCol();
     row = sheet.getRow(lastRow+1); if (row == null) row = sheet.createRow(lastRow+1);
     cell = row.getCell(col); if (cell == null) cell = row.createCell(col);
     if (ser < seriesData.length) cell.setCellValue(seriesData[ser]);
     else cell.setCellValue(0); // Here we need set 0 where it not should be needed.

     XDDFNumericalDataSource<Double> values = XDDFDataSourcesFactory.fromNumericCellRange(
                                               sheet, 
                                               new CellRangeAddress(firstNumCell.getRow(), lastRow+1, col, col));

     series.replaceData(category, values);
    }
   }
   ser++;
  }
  chart.plot(chartData);
 }

 public static void main(String[] args) throws Exception {

  XSSFWorkbook workbook = (XSSFWorkbook)WorkbookFactory.create(new FileInputStream("WorkbookWithChart.xlsx"));

  XSSFSheet sheet = workbook.getSheetAt(0);

  XSSFChart chart = getChartWithTitle(sheet, "Z-Acceleration"); 

  if (chart != null) {
   addMonthDataToChart(sheet, chart, "Apr", new Double[]{7d,3d,5d});
   addMonthDataToChart(sheet, chart, "Mai", new Double[]{2d,6d,8d});
   addMonthDataToChart(sheet, chart, "Jun", new Double[]{1d,9d,4d});
   addMonthDataToChart(sheet, chart, "Jul", new Double[]{5d,6d});
  }

  FileOutputStream out = new FileOutputStream("WorkbookWithChartNew.xlsx");
  workbook.write(out);
  out.close();
  workbook.close();

 }
}
...