POI bar chart generate one series has question

馋奶兔 提交于 2021-01-28 05:10:08

问题


I use JDK8 and POI-4.1.0 use they example here a link

export chart to Word .when create two series is ok two series img, but I only create one series .the chart mistake category for series one series img

"lang1" "lang2" "lang3" is category name but they become series name. i have no ideal. I also find use line chart have the same problem my code
    public static void main(String[] args) throws Exception {

            List<String> listLanguages = new ArrayList<>(3);
            listLanguages.add("lang1");listLanguages.add("lang2");listLanguages.add("lang3");

            List<Double> listCountries = new ArrayList<>(3);            
            listCountries.add(10d);listCountries.add(20d);listCountries.add(30d);

            List<Double> listSpeakers = new ArrayList<>(3);
            listSpeakers.add(14d);listSpeakers.add(25d);listSpeakers.add(33d);

            String[] categories = listLanguages.toArray(new String[listLanguages.size()]);
            Double[] values1 = listCountries.toArray(new Double[listCountries.size()]);
            Double[] values2 = listSpeakers.toArray(new Double[listSpeakers.size()]);

            try (XWPFDocument doc = new XWPFDocument()) {
                XWPFChart chart = doc.createChart(5000000, 4000000);
                XDDFChartAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);

                XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);

                leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
                leftAxis.setCrossBetween(AxisCrossBetween.BETWEEN);
                final int numOfPoints = categories.length;
                final String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0));
                final String valuesDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 1, 1));
                final String valuesDataRange2 = chart.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);
                XDDFBarChartData bar = (XDDFBarChartData) chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
                XDDFBarChartData.Series series1 = (XDDFBarChartData.Series) bar.addSeries(categoriesData, valuesData);
                series1.setTitle("a",chart.setSheetTitle("a", 1));

                 //XDDFBarChartData.Series series2 = (XDDFBarChartData.Series) bar.addSeries(categoriesData, valuesData2);
                //series2.setTitle("b",chart.setSheetTitle("b", 2)); 

                bar.setVaryColors(true);
                bar.setBarDirection(BarDirection.COL);
                chart.plot(bar);

                XDDFChartLegend legend = chart.getOrAddLegend();
                legend.setPosition(LegendPosition.LEFT);
               // legend.setOverlay(false);
               try (OutputStream out = new FileOutputStream("C:/Users/lyf/Desktop/barExample.docx")) {
                    doc.write(out);
                }
            }
            catch(Exception e)
            {  
            }   
    }

回答1:


The main problem is that setting setVaryColors to true means the following:

If only one series, then do varying the colors for each data point of the series. Then the legend shows the varying data points instead of the series. If more than one series, then do varying the colors for each series. Then the legend shows the varying series.

So we need set setVaryColors to false if only one series is present.

But additional we need set AxisCrossBetween, so the left axis crosses the category axis between the categories. Else first and last category is exactly on cross points and the bars are only half visible.

And at last, XDDF is only half ready at the moment and there are many bugs. For example XDDFChart.setSheetTitle is buggy. It creates a Table but only half way and incomplete. Excel cannot opening the workbook after creating that incomplete Table. So updating the chart data in Word is not possible.

The following code works for me and can create a chart with only one series as well as with two. Additional I have tried making the code more structured in single steps. Each step is commented on what it is doing.

import java.io.*;

import org.apache.poi.xwpf.usermodel.*;

import org.apache.poi.ss.util.*;
import org.apache.poi.util.Units;

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

import org.apache.poi.xssf.usermodel.*;

public class CreateWordXDDFChart {

 // Methode to set title in the data sheet without creating a Table but using the sheet data only.
 // Creating a Table is not really necessary.
 static CellReference setTitleInDataSheet(XWPFChart chart, String title, int column) throws Exception {
  XSSFWorkbook workbook = chart.getWorkbook();
  XSSFSheet sheet = workbook.getSheetAt(0);
  XSSFRow row = sheet.getRow(0); if (row == null) row = sheet.createRow(0);
  XSSFCell cell = row.getCell(column); if (cell == null) cell = row.createCell(column);
  cell.setCellValue(title);
  return new CellReference(sheet.getSheetName(), 0, column, true, true);
 }

 public static void main(String[] args) throws Exception {
  try (XWPFDocument document = new XWPFDocument()) {

   // create the data
   String[] categories = new String[]{"Lang 1", "Lang 2", "Lang 3"};
   Double[] valuesA = new Double[]{10d, 20d, 30d};
   Double[] valuesB = new Double[]{15d, 25d, 35d};

   // 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 valuesDataRangeA = chart.formatRange(new CellRangeAddress(1, numOfPoints, 1, 1));
   String valuesDataRangeB = chart.formatRange(new CellRangeAddress(1, numOfPoints, 2, 2));
   XDDFDataSource<String> categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange, 0);
   XDDFNumericalDataSource<Double> valuesDataA = XDDFDataSourcesFactory.fromArray(valuesA, valuesDataRangeA, 1);
   XDDFNumericalDataSource<Double> valuesDataB = XDDFDataSourcesFactory.fromArray(valuesB, valuesDataRangeB, 2);

   // create axis
   XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
   XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
   leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
   // Set AxisCrossBetween, so the left axis crosses the category axis between the categories.
   // Else first and last category is exactly on cross points and the bars are only half visible.
   leftAxis.setCrossBetween(AxisCrossBetween.BETWEEN);

   // create chart data
   XDDFChartData data = chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
   ((XDDFBarChartData)data).setBarDirection(BarDirection.COL);

   // create series
   // if only one series do not vary colors for each bar
   ((XDDFBarChartData)data).setVaryColors(false);
   XDDFChartData.Series series = data.addSeries(categoriesData, valuesDataA);
   // XDDFChart.setSheetTitle is buggy. It creates a Table but only half way and incomplete. 
   // Excel cannot opening the workbook after creatingg that incomplete Table. 
   // So updating the chart data in Word is not possible.
   //series.setTitle("a", chart.setSheetTitle("a", 1));
   series.setTitle("a", setTitleInDataSheet(chart, "a", 1));

/*
   // if more than one series do vary colors of the series
   ((XDDFBarChartData)data).setVaryColors(true);
   series = data.addSeries(categoriesData, valuesDataB);
   //series.setTitle("b", chart.setSheetTitle("b", 2));
   series.setTitle("b", setTitleInDataSheet(chart, "b", 2));
*/

   // plot chart data
   chart.plot(data);

   // create legend
   XDDFChartLegend legend = chart.getOrAddLegend();
   legend.setPosition(LegendPosition.LEFT);
   legend.setOverlay(false);

   // Write the output to a file
   try (FileOutputStream fileOut = new FileOutputStream("CreateWordXDDFChart.docx")) {
    document.write(fileOut);
   }
  }
 }
}


来源:https://stackoverflow.com/questions/56054458/poi-bar-chart-generate-one-series-has-question

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!