如何在 POI 4.0.1 中将带有第二个轴的第二行添加到 XDDFChart? [英] How do I add a second line with a SECOND AXIS to an XDDFChart in POI 4.0.1?

查看:35
本文介绍了如何在 POI 4.0.1 中将带有第二个轴的第二行添加到 XDDFChart?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am unable to add a line on a second axis (right axis) on an existing chart. Is there a way to do this with the new implementation of Charts in POI 4.0.0/1?

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

Desired output will look like this (A simple excel chart with 2 axes): . The associated data to that chart as an example:

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

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

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

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

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

Here is the code that I am trying so far in Java, it is mostly replicated from the 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);

运行此代码不会产生任何编译错误.但是我在打开文档内容有问题"时出现错误.

Running this code doesn't produce any compile errors. But I do get errors when opening the document "Problem with its' contents."

我想我没有正确添加第二行和第二轴.

I suppose I am not adding the 2nd line and 2nd axes correctly.

有没有办法做到这一点?

Is there a way to accomplish this?

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

Update w. Solution Axel's solution below works perfectly. The additional info to know is exactly what was the issue.

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

I would also like to recognize the order in which you add to the plot, this will hopefully help others

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

推荐答案

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

When it comes to multiple different value axes in one chart, this is not fully implemented in XDDF until now. So we need correcting something using the low level ooxml-schemas-1.4 classes.

所需知识:

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

In principle the series which shall be shown on second value axis are in a separate chart in the same plot area. So the series which shall be shown on second value axis needs it's own bottom axis too. But this bottom axis must be invisible.

轴、第二个底部和新的右轴必须正确交叉.这个穿越 apache poi 直到现在还不能正常工作.所以我们必须在这里纠正.

Both the axes, the second bottom and the new right axis, must cross each other properly. This crossing apache poi does not properly until now. So we must correct here.

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

Because while adding to the chart, the apache poi code which adds the second line chart does not knows something about the already present line chart, it's IDs starts with 0 again. But this is wrong for an combined chart. So we need correct the id and order. It must not start with 0 again because there is a line series already in same plot area.

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

Complete example to be reproducible for others too:

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);
 }
}

这篇关于如何在 POI 4.0.1 中将带有第二个轴的第二行添加到 XDDFChart?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆