【问题标题】:How do I add a second line with a SECOND AXIS to an XDDFChart in POI 4.0.1?如何在 POI 4.0.1 中将带有 SECOND AXIS 的第二行添加到 XDDFChart?
【发布时间】:2019-03-16 02:09:24
【问题描述】:

我无法在现有图表的第二个轴(右轴)上添加一条线。有没有办法在 POI 4.0.0/1 中使用 Charts 的新实现来做到这一点?

所需的输出将如下所示(带有 2 个轴的简单 excel 图表): .以该图表的关联数据为例:

系列 1/Axis1 = [1, 2, 3, 4, 5, 6, 7, 8, 9]

系列 2/Axis2 = [200,300,400,500,600,700,800,900,1000]

这是我目前在 Java 中尝试的代码,它主要是从 LineChart.java example 复制而来的

//Initial code instantiates a document
XWPFDocument doc = new XWPFDocument();
...


//  Generate Chart
//  This was taken from the example https://svn.apache.org/repos/asf/poi/trunk/src/examples/src/org/apache/poi/xssf/usermodel/examples/LineChart.java
XWPFChart prChart = doc.createChart();

//Values 1 on the Left Axis
//Values 2 on the Right Axis
String[] categories = dates.toArray(new String[dates.size()]);
BigDecimal[] values1 = prices1.toArray(new BigDecimal[prices1.size()]);
BigDecimal[] values2 = prices2.toArray(new BigDecimal[prices2.size()]);

XDDFChartAxis bottomAxis = prChart.createCategoryAxis(AxisPosition.BOTTOM);
bottomAxis.setMajorTickMark(AxisTickMark.NONE);

XDDFValueAxis leftAxis = prChart.createValueAxis(AxisPosition.LEFT);
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
leftAxis.setMajorTickMark(AxisTickMark.OUT);

/*
 * Is this made correctly?  
 */
XDDFValueAxis rightAxis = prChart.createValueAxis(AxisPosition.RIGHT);
rightAxis.setCrosses(AxisCrosses.MAX);
rightAxis.setMajorTickMark(AxisTickMark.IN);


final int numOfPoints = categories.length;
final String categoryDataRange = prChart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0));
final String valuesDataRange = prChart.formatRange(new CellRangeAddress(1, numOfPoints, 1, 1));
final String valuesDataRange2 = prChart.formatRange(new CellRangeAddress(1, numOfPoints, 2, 2));

final XDDFDataSource<?> categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange, 0);
final XDDFNumericalDataSource<? extends Number> valuesData = XDDFDataSourcesFactory.fromArray(values1, valuesDataRange, 1);
final XDDFNumericalDataSource<? extends Number> valuesData2 = XDDFDataSourcesFactory.fromArray(values2, valuesDataRange2, 2);

XDDFLineChartData line = (XDDFLineChartData) prChart.createData(ChartTypes.LINE, bottomAxis, leftAxis);
XDDFLineChartData.Series series1 = (XDDFLineChartData.Series) line.addSeries(categoriesData, valuesData);            
series1.setTitle("Price", null);
series1.setSmooth(true);
series1.setMarkerStyle(MarkerStyle.NONE);
solidLineSeries(series1, PresetColor.BLUE_VIOLET);

// Am I adding the rightAxis correctly here?
XDDFLineChartData line2 = (XDDFLineChartData) prChart.createData(ChartTypes.LINE, bottomAxis, rightAxis);
XDDFLineChartData.Series series2 = (XDDFLineChartData.Series) line2.addSeries(categoriesData, valuesData2);
series2.setTitle("Index", null);
series2.setSmooth(true);
series2.setMarkerStyle(MarkerStyle.NONE);
solidLineSeries(series2, PresetColor.BLACK);


prChart.plot(line);
prChart.plot(line2);        ///     <- Does this add to the same plot correctly?
prChart.displayBlanksAs(DisplayBlanks.GAP);

运行此代码不会产生任何编译错误。但是在打开文档“内容有问题”时,我确实遇到了错误。

我想我没有正确添加第二条线和第二条轴。

有没有办法做到这一点?

更新 w。解决方案 Axel 下面的解决方案非常有效。要知道的其他信息正是问题所在。

我也想知道你添加到情节中的顺序,这希望对其他人有所帮助

  1. 创建第一组轴
  2. 创建第一行
  3. 绘制第一行
  4. 创建新轴
  5. 创建第二行
  6. 绘制第二行
  7. 更新轴 ID!

【问题讨论】:

    标签: java charts apache-poi


    【解决方案1】:

    当涉及到一个图表中的多个不同值轴时,XDDF 直到现在还没有完全实现。所以我们需要使用低级别的ooxml-schemas-1.4 类来纠正一些问题。

    需要的知识:

    原则上,应在第二个值轴上显示的系列在同一绘图区域的单独图表中。所以应该在第二个值轴上显示的系列也需要它自己的底轴。但是这个底轴一定是不可见的。

    两个轴,第二个底部和新的右轴,必须正确交叉。这个交叉apache poi 直到现在才正确。所以我们必须在这里更正。

    因为在添加到图表时,添加第二个折线图的apache poi 代码对已经存在的折线图不了解,它的 ID 再次以 0 开头。但这对于组合图表是错误的。所以我们需要更正 id 和 order。它不能再次以 0 开头,因为在同一绘图区域中已经有一个线系列。

    完整的示例也可供其他人重现:

    import java.io.*;
    
    import org.apache.poi.xwpf.usermodel.*;
    
    import org.apache.poi.ss.util.CellRangeAddress;
    import org.apache.poi.util.Units;
    
    import org.apache.poi.xddf.usermodel.*;
    import org.apache.poi.xddf.usermodel.chart.*;
    
    public class CreateWordXDDFChart {
    
     public static void main(String[] args) throws Exception {
      try (XWPFDocument document = new XWPFDocument()) {
    
       // create the data
       String[] categories = new String[]{"1","2","3","4","5","6","7","8","9"};
       Double[] values1 = new Double[]{1d,2d,3d,4d,5d,6d,7d,8d,9d};
       Double[] values2 = new Double[]{200d,300d,400d,500d,600d,700d,800d,900d,1000d};
    
       // create the chart
       XWPFChart chart = document.createChart(15*Units.EMU_PER_CENTIMETER, 10*Units.EMU_PER_CENTIMETER);
    
       // create data sources
       int numOfPoints = categories.length;
       String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0));
       String valuesDataRange1 = chart.formatRange(new CellRangeAddress(1, numOfPoints, 1, 1));
       String valuesDataRange2 = chart.formatRange(new CellRangeAddress(1, numOfPoints, 2, 2));
       XDDFDataSource<String> categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange, 0);
       XDDFNumericalDataSource<Double> valuesData1 = XDDFDataSourcesFactory.fromArray(values1, valuesDataRange1, 1);
       XDDFNumericalDataSource<Double> valuesData2 = XDDFDataSourcesFactory.fromArray(values2, valuesDataRange2, 2);
    
       // first line chart
       XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
       XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
       leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
       XDDFChartData data = chart.createData(ChartTypes.LINE, bottomAxis, leftAxis);
       XDDFChartData.Series series = data.addSeries(categoriesData, valuesData1);
       chart.plot(data);
    
       solidLineSeries(data, 0, PresetColor.BLUE);
    
       // second line chart
       // bottom axis must be there but must not be visible
       bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
       bottomAxis.setVisible(false);
    
       XDDFValueAxis rightAxis = chart.createValueAxis(AxisPosition.RIGHT);
       rightAxis.setCrosses(AxisCrosses.MAX);
    
       // set correct cross axis
       bottomAxis.crossAxis(rightAxis);
       rightAxis.crossAxis(bottomAxis);
    
       data = chart.createData(ChartTypes.LINE, bottomAxis, rightAxis);
       series = data.addSeries(categoriesData, valuesData2);
       chart.plot(data);
    
       // correct the id and order, must not be 0 again because there is one line series already
       chart.getCTChart().getPlotArea().getLineChartArray(1).getSerArray(0).getIdx().setVal(1);
       chart.getCTChart().getPlotArea().getLineChartArray(1).getSerArray(0).getOrder().setVal(1);
    
       solidLineSeries(data, 0, PresetColor.RED);
    
       // Write the output to a file
       try (FileOutputStream fileOut = new FileOutputStream("CreateWordXDDFChart.docx")) {
        document.write(fileOut);
       }
      }
     }
    
     private static void solidLineSeries(XDDFChartData data, int index, PresetColor color) {
      XDDFSolidFillProperties fill = new XDDFSolidFillProperties(XDDFColor.from(color));
      XDDFLineProperties line = new XDDFLineProperties();
      line.setFillProperties(fill);
      XDDFChartData.Series series = data.getSeries().get(index);
      XDDFShapeProperties properties = series.getShapeProperties();
      if (properties == null) {
       properties = new XDDFShapeProperties();
      }
      properties.setLineProperties(line);
      series.setShapeProperties(properties);
     }
    }
    

    【讨论】:

    • 我需要这个解决方案,但需要 Excel 工作表!我尝试将XSSFWorkbookXSSFSheetXSSFDrawing 一起使用,它确实绘制了图表,但Y 轴上只有一条带有0 值的线。还有什么我需要适应的吗?
    • @user2071938:所以问一个关于这个的问题。显示您拥有的代码。展示它的结果。并展示你期望的结果。
    • 见我的question
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-12
    • 2011-03-30
    相关资源
    最近更新 更多