Second Line in an Apache-POI chart with seperate axis - java
Hi this code taken from the answer here is working as expected, but I want exactly the same Chart but in an Excel-Sheet
package eu.flexsolution.task.excel;
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 TEst {
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);
}
}
So I modified the code like this to get an XLSX document, but the Chart isn't the same
package eu.flexsolution.task.excel;
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.*;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class TEst {
public static void main(String[] args) throws Exception {
try (XSSFWorkbook document = new XSSFWorkbook()) {
XSSFSheet chartSheet = document.createSheet("chart");
// 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
XSSFDrawing drawing = chartSheet.createDrawingPatriarch();
XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 0, 26, 40);
XDDFChart chart = drawing.createChart(anchor);
// 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.xlsx")) {
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);
}
}
The problem is that for a Word (XWPF) chart, the data are stored in a Excel workbook which is embedded in the Word file. There the data can be given as arrays and handled via XDDFDataSourcesFactory.fromArray. This then fills the embedded Excel data sheet.
But for a Excel(XSSF) chart the data needs to be in a Excel data sheet. Of course Excel will not embedding a Excel sheet in it's files as it has worksheets already. So for Excel the data needs to be in a worksheet and needs to be handled via XDDFDataSourcesFactory.fromStringCellRange or XDDFDataSourcesFactory.fromNumericCellRange then.
Complete example which creates the Excel XSSFChart:
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.*;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class CreateExcelXDDFChart {
public static void main(String[] args) throws Exception {
try (XSSFWorkbook document = new XSSFWorkbook()) {
XSSFSheet chartSheet = document.createSheet("chart");
XSSFSheet dataSheet = document.createSheet("data");
// create the data
String[] categories = new String[] { "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9" };
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 };
int r = 0;
for (String cat : categories) {
dataSheet.createRow(r).createCell(0).setCellValue(cat);
dataSheet.getRow(r).createCell(1).setCellValue(values1[r]);
dataSheet.getRow(r).createCell(2).setCellValue(values2[r]);
r++;
}
// create the chart
XSSFDrawing drawing = chartSheet.createDrawingPatriarch();
XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 0, 26, 40);
XDDFChart chart = drawing.createChart(anchor);
// create data sources
int numOfPoints = categories.length;
XDDFDataSource<String> categoriesData = XDDFDataSourcesFactory.fromStringCellRange(dataSheet, new CellRangeAddress(0, numOfPoints-1, 0, 0));
XDDFNumericalDataSource<Double> valuesData1 = XDDFDataSourcesFactory.fromNumericCellRange(dataSheet, new CellRangeAddress(0, numOfPoints-1, 1, 1));
XDDFNumericalDataSource<Double> valuesData2 = XDDFDataSourcesFactory.fromNumericCellRange(dataSheet, new CellRangeAddress(0, numOfPoints-1, 2, 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("CreateExcelXDDFChart.xlsx")) {
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);
XDDFChartData.Series series = data.getSeries(index);
XDDFShapeProperties properties = series.getShapeProperties();
if (properties == null) {
properties = new XDDFShapeProperties();
}
properties.setLineProperties(line);
series.setShapeProperties(properties);
}
}
Works using current apache poi 4.1.2.
Related
Setting axis title and adding values to the bar
I need to change the properties (like size of font) of the title axis (circled in red) I'm using the apache-poi library and here's the part of the code which generates both titles XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM); bottomAxis.setTitle("Client Name"); XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT); leftAxis.setTitle("Value ("+clientDataObjectRequest.getDataFormatCodeValue()+")"); leftAxis.setCrosses(AxisCrosses.AUTO_ZERO); leftAxis.setCrossBetween(AxisCrossBetween.BETWEEN); I've already tried to use the method getOrAddTextProperties().setFontSize() but this change only the size of the labels (client names and numbers) and not the title itself. In yellow, I would like to add the precisely number of the value. For example, the value of "client 3" is 53,78, so I need to put this value in the chart just after the end of the bar (marked as yellow).
The question about formatting the axes titles is answered already. See Resizing legend and axis title in a apache poi bar chart. "To add the precisely number of the value" to the data bars means to have data labels set. This is not directly supported ny Apache POI until now. So using low level org.openxmlformats.schemas.drawingml.x2006.chart.* classes is necessary. See https://stackoverflow.com/search?tab=newest&q=user%3a3915431%20add%20data%20labels. Of course this is not very comfortable. So I have tried to create a method void setDataLabels(XDDFChartData.Series series, int pos, boolean... show) for this. There int pos can be one of the following: INT_BEST_FIT 1 - positions data label at best fit to be readable INT_B 2 - positions data label at bottom INT_CTR 3 - positions data label at center INT_IN_BASE 4 - positions data label inside at base INT_IN_END 5 - positions data label inside at the end INT_L 6 - positions data label at left INT_OUT_END 7 - positions data label outside at the end INT_R 8 - positions data label at right INT_T 9 - positions data label at top Not all chart types support all positions. The boolean... show is a list of boolean to tell what shall visible (true) or not visible (false) in the data labels. Sequence is as follows: ShowVal, ShowLegendKey, ShowCatName, ShowSerName, ShowPercent, ShowBubbleSize, ShowLeaderLines Complete example: import java.io.FileOutputStream; import java.io.IOException; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.xddf.usermodel.PresetColor; import org.apache.poi.xddf.usermodel.XDDFColor; import org.apache.poi.xddf.usermodel.XDDFShapeProperties; import org.apache.poi.xddf.usermodel.XDDFSolidFillProperties; import org.apache.poi.xddf.usermodel.chart.AxisCrosses; import org.apache.poi.xddf.usermodel.chart.AxisCrossBetween; import org.apache.poi.xddf.usermodel.chart.AxisPosition; import org.apache.poi.xddf.usermodel.chart.BarDirection; import org.apache.poi.xddf.usermodel.chart.ChartTypes; import org.apache.poi.xddf.usermodel.chart.LegendPosition; import org.apache.poi.xddf.usermodel.chart.XDDFBarChartData; import org.apache.poi.xddf.usermodel.chart.XDDFLineChartData; import org.apache.poi.xddf.usermodel.chart.XDDFPieChartData; import org.apache.poi.xddf.usermodel.chart.XDDFCategoryAxis; import org.apache.poi.xddf.usermodel.chart.XDDFChartData; import org.apache.poi.xddf.usermodel.chart.XDDFChartLegend; import org.apache.poi.xddf.usermodel.chart.XDDFDataSource; import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory; import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource; import org.apache.poi.xddf.usermodel.chart.XDDFValueAxis; import org.apache.poi.xddf.usermodel.chart.XDDFTitle; import org.apache.poi.xssf.usermodel.XSSFChart; import org.apache.poi.xssf.usermodel.XSSFClientAnchor; import org.apache.poi.xssf.usermodel.XSSFDrawing; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; /** * Bar chart example. */ public final class BarChart { private BarChart() {} public static void main(String[] args) throws IOException { Object[][] chartData = new Object[][]{ new Object[]{"Client 1", "Client 2", "Client 3", "Client 4", "Client 5", "Client 6", "Client 7", "Client 8"}, new Object[]{12, 2.345d, 34, 6, 45.6d, .456d, 50.5d, 12.345d}, }; try (XSSFWorkbook wb = new XSSFWorkbook()) { XSSFSheet sheet = wb.createSheet("barchart"); // put sheet data Row row; Cell cell; int rowIndex = 0; int colIndex = 0; for (Object[] dataRow : chartData) { row = sheet.createRow((short) rowIndex); colIndex = 0; for (Object value : dataRow) { cell = row.createCell((short) colIndex); if (value instanceof String) cell.setCellValue((String)value); if (value instanceof Number) cell.setCellValue(((Number)value).doubleValue()); colIndex++; } rowIndex++; } // create the chart // chart data sources XDDFDataSource<String> xs = XDDFDataSourcesFactory.fromStringCellRange(sheet, new CellRangeAddress(0, 0, 0, colIndex - 1)); XDDFNumericalDataSource<Double> ys1 = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(1, 1, 0, colIndex - 1)); // chart in drawing XSSFDrawing drawing = sheet.createDrawingPatriarch(); XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 3, 8, 20); XSSFChart chart = drawing.createChart(anchor); chart.setTitleText("Turnover Rate"); chart.setTitleOverlay(false); chart.getFormattedTitle().getParagraph(0).addDefaultRunProperties().setFontSize(20d); // category axis XDDFCategoryAxis catAxis = chart.createCategoryAxis(AxisPosition.BOTTOM); XDDFTitle title = getOrSetAxisTitle(catAxis); title.setOverlay(false); title.setText("Client Name"); title.getBody().getParagraph(0).addDefaultRunProperties().setFontSize(12d); // value axis XDDFValueAxis valAxis = chart.createValueAxis(AxisPosition.LEFT); title = getOrSetAxisTitle(valAxis); title.setOverlay(false); title.setText("Value (Percentage)"); title.getBody().getParagraph(0).addDefaultRunProperties().setFontSize(12d); // cross axes valAxis.setCrosses(AxisCrosses.AUTO_ZERO); valAxis.setCrossBetween(AxisCrossBetween.BETWEEN); // chart data XDDFChartData data = chart.createData(ChartTypes.BAR, catAxis, valAxis); // series XDDFChartData.Series series1 = data.addSeries(xs, ys1); series1.setTitle("Series 1", null); // there must be a series title even if no legend is needed // add data labels setDataLabels(series1, 7, true); // pos 7 = INT_OUT_END, showVal = true // plot chart chart.plot(data); // in order to transform a bar chart into a column chart, you just need to change the bar direction XDDFBarChartData bar = (XDDFBarChartData) data; // bar.setBarDirection(BarDirection.COL); // set series fill color solidFillSeries(data, 0, PresetColor.BLUE); // Write the output to a file try (FileOutputStream fileOut = new FileOutputStream("./ooxml-bar-chart.xlsx")) { wb.write(fileOut); } } } private static void solidFillSeries(XDDFChartData data, int index, PresetColor color) { XDDFSolidFillProperties fill = new XDDFSolidFillProperties(XDDFColor.from(color)); XDDFChartData.Series series = data.getSeries(index); XDDFShapeProperties properties = series.getShapeProperties(); if (properties == null) { properties = new XDDFShapeProperties(); } properties.setFillProperties(fill); series.setShapeProperties(properties); } private static XDDFTitle getOrSetAxisTitle(XDDFValueAxis axis) { try { java.lang.reflect.Field _ctValAx = XDDFValueAxis.class.getDeclaredField("ctValAx"); _ctValAx.setAccessible(true); org.openxmlformats.schemas.drawingml.x2006.chart.CTValAx ctValAx = (org.openxmlformats.schemas.drawingml.x2006.chart.CTValAx)_ctValAx.get(axis); if (!ctValAx.isSetTitle()) { ctValAx.addNewTitle(); } XDDFTitle title = new XDDFTitle(null, ctValAx.getTitle()); return title; } catch (Exception ex) { ex.printStackTrace(); return null; } } private static XDDFTitle getOrSetAxisTitle(XDDFCategoryAxis axis) { try { java.lang.reflect.Field _ctCatAx = XDDFCategoryAxis.class.getDeclaredField("ctCatAx"); _ctCatAx.setAccessible(true); org.openxmlformats.schemas.drawingml.x2006.chart.CTCatAx ctCatAx = (org.openxmlformats.schemas.drawingml.x2006.chart.CTCatAx)_ctCatAx.get(axis); if (!ctCatAx.isSetTitle()) { ctCatAx.addNewTitle(); } XDDFTitle title = new XDDFTitle(null, ctCatAx.getTitle()); return title; } catch (Exception ex) { ex.printStackTrace(); return null; } } private static void setDataLabels(XDDFChartData.Series series, int pos, boolean... show) { /* INT_BEST_FIT 1 INT_B 2 INT_CTR 3 INT_IN_BASE 4 INT_IN_END 5 INT_L 6 INT_OUT_END 7 INT_R 8 INT_T 9 */ try { org.openxmlformats.schemas.drawingml.x2006.chart.CTDLbls ctDLbls = null; if (series instanceof XDDFBarChartData.Series) { java.lang.reflect.Field _ctBarSer = XDDFBarChartData.Series.class.getDeclaredField("series"); _ctBarSer.setAccessible(true); org.openxmlformats.schemas.drawingml.x2006.chart.CTBarSer ctBarSer = (org.openxmlformats.schemas.drawingml.x2006.chart.CTBarSer)_ctBarSer.get((XDDFBarChartData.Series)series); if (ctBarSer.isSetDLbls()) ctBarSer.unsetDLbls(); ctDLbls = ctBarSer.addNewDLbls(); if (!(pos == 3 || pos == 4 || pos == 5 || pos == 7)) pos = 3; // bar chart does not provide other pos ctDLbls.addNewDLblPos().setVal(org.openxmlformats.schemas.drawingml.x2006.chart.STDLblPos.Enum.forInt(pos)); } else if (series instanceof XDDFLineChartData.Series) { java.lang.reflect.Field _ctLineSer = XDDFLineChartData.Series.class.getDeclaredField("series"); _ctLineSer.setAccessible(true); org.openxmlformats.schemas.drawingml.x2006.chart.CTLineSer ctLineSer = (org.openxmlformats.schemas.drawingml.x2006.chart.CTLineSer)_ctLineSer.get((XDDFLineChartData.Series)series); if (ctLineSer.isSetDLbls()) ctLineSer.unsetDLbls(); ctDLbls = ctLineSer.addNewDLbls(); if (!(pos == 3 || pos == 6 || pos == 8 || pos == 9 || pos == 2)) pos = 3; // line chart does not provide other pos ctDLbls.addNewDLblPos().setVal(org.openxmlformats.schemas.drawingml.x2006.chart.STDLblPos.Enum.forInt(pos)); } else if (series instanceof XDDFPieChartData.Series) { java.lang.reflect.Field _ctPieSer = XDDFPieChartData.Series.class.getDeclaredField("series"); _ctPieSer.setAccessible(true); org.openxmlformats.schemas.drawingml.x2006.chart.CTPieSer ctPieSer = (org.openxmlformats.schemas.drawingml.x2006.chart.CTPieSer)_ctPieSer.get((XDDFPieChartData.Series)series); if (ctPieSer.isSetDLbls()) ctPieSer.unsetDLbls(); ctDLbls = ctPieSer.addNewDLbls(); if (!(pos == 3 || pos == 1 || pos == 4 || pos == 5)) pos = 3; // pie chart does not provide other pos ctDLbls.addNewDLblPos().setVal(org.openxmlformats.schemas.drawingml.x2006.chart.STDLblPos.Enum.forInt(pos)); }// else if ... if (ctDLbls != null) { ctDLbls.addNewShowVal().setVal((show.length>0)?show[0]:false); ctDLbls.addNewShowLegendKey().setVal((show.length>1)?show[1]:false); ctDLbls.addNewShowCatName().setVal((show.length>2)?show[2]:false); ctDLbls.addNewShowSerName().setVal((show.length>3)?show[3]:false); ctDLbls.addNewShowPercent().setVal((show.length>4)?show[4]:false); ctDLbls.addNewShowBubbleSize().setVal((show.length>5)?show[5]:false); ctDLbls.addNewShowLeaderLines().setVal((show.length>6)?show[6]:false); } } catch (Exception ex) { ex.printStackTrace(); } } } Result:
Apache POI: How to set THIS_YEAR filter in row label date of a pivot
I am trying to create a pivot in excel using apache poi. My requirement is to apply the Date Filter (THIS YEAR) i.e to show only current year dates in the date row label in the pivot. Problem: Below the reproducible code that generates output excel file with "THIS_YEAR" filter applied to the date column but for some reason, it's not showing any data. Below is the code to generate the output. import java.io.FileOutputStream; import org.apache.poi.ss.*; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.*; import org.apache.poi.xssf.usermodel.*; import org.openxmlformats.schemas.spreadsheetml.x2006.main.*; import java.util.GregorianCalendar; class CreatePivotTableFilter { public static void main(String[] args) throws Exception { try (Workbook workbook = new XSSFWorkbook(); FileOutputStream fileout = new FileOutputStream("Excel.xlsx") ) { DataFormat format = workbook.createDataFormat(); CellStyle dateStyle = workbook.createCellStyle(); dateStyle.setDataFormat(format.getFormat("M\\/d\\/yy")); Sheet sheet = workbook.createSheet(); String[] headers = new String[]{"Column1", "Column2", "Date", "Count"}; Row row = sheet.createRow(0); Cell cell; for (int c = 0; c < headers.length; c++) { cell = row.createCell(c); cell.setCellValue(headers[c]); } Object[][] data = new Object[][]{ new Object[]{"A", "B1", new GregorianCalendar(2020, 0, 2), 2d}, new Object[]{"A", "B2", new GregorianCalendar(2020, 0, 1), 4d}, new Object[]{"B", "B1", new GregorianCalendar(2019, 0, 2), 1d}, new Object[]{"B", "B2", new GregorianCalendar(2019, 0, 2), 7d}, new Object[]{"A", "C1", new GregorianCalendar(2019, 0, 1), 5d}, new Object[]{"A", "C2", new GregorianCalendar(2019, 0, 1), 5d}, new Object[]{"B", "C1", new GregorianCalendar(2019, 0, 2), 2d}, new Object[]{"B", "C2", new GregorianCalendar(2019, 0, 2), 8d} }; for (int r = 0; r < data.length; r++) { row = sheet.createRow(r+1); Object[] rowData = data[r]; for (int c = 0; c < rowData.length; c++) { cell = row.createCell(c); if (rowData[c] instanceof String) { cell.setCellValue((String)rowData[c]); } else if (rowData[c] instanceof GregorianCalendar) { cell.setCellValue((GregorianCalendar)rowData[c]); cell.setCellStyle(dateStyle); } else if (rowData[c] instanceof Double) { cell.setCellValue((Double)rowData[c]); } } } XSSFPivotTable pivotTable = ((XSSFSheet)sheet).createPivotTable( new AreaReference("A1:D9", SpreadsheetVersion.EXCEL2007), new CellReference("F4")); pivotTable.addRowLabel(0); pivotTable.addRowLabel(1); pivotTable.addRowLabel(2); pivotTable.addColumnLabel(DataConsolidateFunction.SUM, 3); pivotTable.addColumnLabel(DataConsolidateFunction.AVERAGE, 3); CTPivotFilters filters = CTPivotFilters.Factory.newInstance(); CTPivotFilter filter = filters.addNewFilter(); filter.setId(0); filter.setFld(2); filter.setType(STPivotFilterType.THIS_YEAR); CTFilterColumn filterColumn = filter.addNewAutoFilter().addNewFilterColumn(); filterColumn.setColId(0); CTFilters ctFilters = filterColumn.addNewFilters(); ctFilter.addNewFilter().setVal("This Year Filter"); //set filters to pivot table definition pivotTable.getCTPivotTableDefinition().setFilters(filters); workbook.write(fileout); } } } Output generated by code Expected Output
Best way dealing with the low level ooxml-schemas classes is creating whatever one wants using Excel's GUI, then unzipping the resulting *.xlsx file and having a look at the XML what the GUI has created. In that case in xl/pivotTables/pivotTable1.xml we find: ... <filters> <filter fld="2" type="thisYear" id="0"> <autoFilter> <filterColumn colId="0"> <dynamicFilter type="thisYear"/> </filterColumn> </autoFilter> </filter> </filters> ... So filterColumn does not contain <filters><filter ...> but contains dynamicFilter of type thisYear. So your code would must be: ... CTPivotFilters filters = CTPivotFilters.Factory.newInstance(); CTPivotFilter filter = filters.addNewFilter(); filter.setId(0); filter.setFld(2); filter.setType(STPivotFilterType.THIS_YEAR); CTFilterColumn filterColumn = filter.addNewAutoFilter().addNewFilterColumn(); filterColumn.setColId(0); CTDynamicFilter ctDynamicFilter = filterColumn.addNewDynamicFilter(); ctDynamicFilter.setType(STDynamicFilterType.THIS_YEAR); //set filters to pivot table definition pivotTable.getCTPivotTableDefinition().setFilters(filters); ... Unfortunately there is not any API documentation of ooxml-schemas public available. So if we need it, we need to download ooxml-schemas sources from maven. Then we can create a API documentation using javadoc. There then we can find fields and methods of CTFilterColumn for example.
Issue with export CrossTab data into excel sheet using Java Apache POI 3.15 version
I am collecting data from my company portal and trying to export it into an excel sheet. I successfully completed for non-crosstab report data. But the issue is coming when the data has crosstab. Data: I have saved in data in ArrayList best on my best understanding. 1. rowHeader:[Quarter, Subcategory] 2. rowElement:[2016 Q1, 2016 Q2, 2016 Q3, 2016 Q4, Audio Equipment, Cameras, Computers, Electronics - Miscellaneous, TV's, Video Equipment] 3. columnHeader:[Year, 2016] 4. columnHeaders[Quarter, Subcategory, Year, 2016] 5. metricsColumn:[Metrics, Profit, Profit Forecast, Profit Margin, Revenue, Revenue Forecast] 6. listData:[[$9,579, $8,823, 19.42%, $49,320, $ 39,456], [$11,449, $9,619, 20.07%, $57,040, $ 53,047], [$4,901, $3,784, 20.02%, $24,480, $ 21,298], [$12,444, $9,525, 19.89%, $62,576, $ 53,815], [$8,820, $8,059, 20.19%, $43,675, $ 35,814], [$16,375, $12,986, 20.44%, $80,130, $ 70,514], [$8,526, $6,929, 15.78%, $54,020, $ 49,698], [$11,602, $9,578, 17.31%, $67,032, $ 63,680], [$4,675, $4,380, 16.83%, $27,780, $ 25,752], [$11,699, $9,421, 16.60%, $70,469, $ 54,966], [$9,386, $7,179, 17.52%, $53,563, $ 49,974], [$10,150, $9,213, 16.96%, $59,864, $ 48,490], [$8,508, $6,772, 17.16%, $49,571, $ 47,092], [$16,429, $13,529, 18.52%, $88,712, $ 83,389], [$6,009, $5,391, 18.05%, $33,295, $ 29,200], [$11,792, $9,791, 17.21%, $68,534, $ 64,285], [$9,243, $7,952, 17.15%, $53,886, $ 49,953], [$14,282, $11,679, 18.03%, $79,193, $ 74,441], [$10,999, $8,538, 15.60%, $70,511, $ 65,575], [$14,475, $11,433, 16.32%, $88,718, $ 78,515], [$5,765, $5,029, 16.77%, $34,373, $ 31,847], [$11,335, $9,567, 15.28%, $74,168, $ 62,672], [$11,990, $11,230, 16.83%, $71,255, $ 64,414], [$13,864, $11,328, 16.87%, $82,203, $ 70,695]] Till so far, I successfully, completed exporting Headers section. I am struggling, now exporting data into an excel sheet because of the crosstab we need to do column merge and row merge for every Quarters and Years. If someone can help me, to write any sample code/ pseudo code for it, it would be a very very helpful for me. I ave stuck with this issue from the last 4 days. I am not getting how to do this part as I never used any exclsheet API. here I am using APACHE POI -3.15 version. Code till so far, which export Header section: //Create a Row Row headerRow = sheet.createRow(0); //int totalcolumns = rowHeader.size() + columnHeader.size(); int mergeHeader = 0; if(metricsColumn != null) { mergeHeader = 1; } //Create Cells //[Quarter, Subcategory, Year, 2016] int j = rowHeader.size(); int row = 0; for (int i = 0; i < columnHeaders.size(); i++) { Cell cell = headerRow.createCell(i); cell.setCellValue(columnHeaders.get(i)); if(j > 0) { sheet.addMergedRegion(new CellRangeAddress(row, mergeHeader, i, i)); } j--; if (i == columnHeaders.size() - 1 && metricsColumn.size() > 0) { sheet.addMergedRegion(new CellRangeAddress(0,0,i,i + metricsColumn.size() - 2)); } cell.setCellStyle(headerCellStyle); } int k = 0; int rowNum1 = 1; int cellNum1 = 2; Row row1 = sheet.createRow(rowNum1); for (int i = rowHeader.size(); i < metricsColumn.size(); i++) { while (k < metricsColumn.size()) { Cell cell = row1.createCell(cellNum1++); cell.setCellValue(metricsColumn.get(k)); cell.setCellStyle(headerCellStyle); k++; } }
I would make the structures for headers and data a little bit different: The headers for example: String[][] headers = new String[][] { new String[] {"Quarter", "Subcategory", "Year", "2016", null, null, null, null}, new String[] { null, null, "Metrics", "Profit", "Profit Forecast", "Profit Margin", "Revenue", "Revenue Forecast"} }; Maybe List<List<String>> also would be possible. The row headers: String[] quarters = new String[]{"2016 Q1", "2016 Q2", "2016 Q3", "2016 Q4"}; String[] subcategories = new String[]{"Audio Equipment", "Cameras", "Computers", "Electronics - Miscellaneous", "TV's", "Video Equipment"}; Maybe List<String> also would be possible. The values: Double[][] values = new Double[][]{ new Double[]{9579d, 8823d, .1942, 49320d,39456d}, new Double[]{11449d, 9619d, .2007, 57040d, 53047d}, ... new Double[]{13864d, 11328d, .1687, 82203d, 70695d} }; Maybe List<List<Double>> also would be possible. Then I would also provide basic formatting instructions in structures. So it is later possible to set them using loops. For example the basic table structure: int[] columnWidths = new int[]{15*256, 15*256, 15*256, 13*256, 13*256, 13*256, 13*256, 13*256}; For example the headers horizontal alignment: HorizontalAlignment[][] horizontalAlignments = new HorizontalAlignment[][]{ new HorizontalAlignment[]{HorizontalAlignment.LEFT, HorizontalAlignment.CENTER, HorizontalAlignment.LEFT, HorizontalAlignment.CENTER, null, null, null, null}, new HorizontalAlignment[]{null, null, HorizontalAlignment.RIGHT, HorizontalAlignment.CENTER, HorizontalAlignment.CENTER, HorizontalAlignment.CENTER, HorizontalAlignment.CENTER, HorizontalAlignment.CENTER} }; And the merged regions for the headers: int[][] headerMergedRegions = new int[][]{ new int[]{0, 0, 3, 7}, new int[]{0, 1, 0, 0}, new int[]{0, 1, 1, 1} }; Then I would set the much used number formats in default column styles: String[] columnCellStyles = new String[]{"default", "default", "default", "currency", "currency", "percent", "currency", "currency"}; ... // creating default cell style having default font CellStyle defaultCellStyle = workbook.createCellStyle(); defaultCellStyle.setFont(defaultFont); // we need currency style for numbers later CellStyle currency = workbook.createCellStyle(); currency.cloneStyleFrom(defaultCellStyle); currency.setDataFormat(format.getFormat("$#,##0")); // we need percent style for numbers later CellStyle percent = workbook.createCellStyle(); percent.cloneStyleFrom(defaultCellStyle); percent.setDataFormat(format.getFormat("0.00%")); ... // set default column styles for (int c = 0; c < columnCellStyles.length; c++) { String style = columnCellStyles[c]; if ("default".equals(style)) { sheet.setDefaultColumnStyle(c, defaultCellStyle); } else if ("currency".equals(style)) { sheet.setDefaultColumnStyle(c, currency); } else if ("percent".equals(style)) { sheet.setDefaultColumnStyle(c, percent); } } So when we set the values later, the cell styles can come from default column style. For this a method CellStyle getPreferredCellStyle(Cell cell) could be used. For formatting I would rely on methods from CellUtil. So not all needed CellStyles needs to be created on workbook level but can be set to the cell into the loops. For cell borders I would use PropertyTemplate. This makes setting borders much more easier. Complete example: import java.io.FileOutputStream; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFCell; import org.apache.poi.hssf.usermodel.HSSFCell; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellUtil; import org.apache.poi.ss.util.PropertyTemplate; import java.util.Map; import java.util.HashMap; class CreateExcelSpecial { static CellStyle getPreferredCellStyle(Cell cell) { // a method to get the preferred cell style for a cell // this is either the already applied cell style // or if that not present, then the row style (default cell style for this row) // or if that not present, then the column style (default cell style for this column) CellStyle cellStyle = cell.getCellStyle(); // if no explicit cell style applied then cellStyle.getIndex() is 0 for XSSF // or 15 (0xF = the index to the default ExtendedFormatRecord (0xF)) for HSSF if ((cell instanceof XSSFCell && cellStyle.getIndex() == 0) || (cell instanceof HSSFCell && cellStyle.getIndex() == 15)) cellStyle = cell.getRow().getRowStyle(); if (cellStyle == null) cellStyle = cell.getSheet().getColumnStyle(cell.getColumnIndex()); if (cellStyle == null) cellStyle = cell.getCellStyle(); return cellStyle; } public static void main(String[] args) throws Exception { // the data // basic table structure int[] columnWidths = new int[]{15*256, 15*256, 15*256, 13*256, 13*256, 13*256, 13*256, 13*256}; String[] columnCellStyles = new String[]{"default", "default", "default", "currency", "currency", "percent", "currency", "currency"}; // headers content and formatting String[][] headers = new String[][] { new String[] {"Quarter", "Subcategory", "Year", "2016", null, null, null, null}, new String[] { null, null, "Metrics", "Profit", "Profit Forecast", "Profit Margin", "Revenue", "Revenue Forecast"} }; HorizontalAlignment[][] horizontalAlignments = new HorizontalAlignment[][]{ new HorizontalAlignment[]{HorizontalAlignment.LEFT, HorizontalAlignment.CENTER, HorizontalAlignment.LEFT, HorizontalAlignment.CENTER, null, null, null, null}, new HorizontalAlignment[]{null, null, HorizontalAlignment.RIGHT, HorizontalAlignment.CENTER, HorizontalAlignment.CENTER, HorizontalAlignment.CENTER, HorizontalAlignment.CENTER, HorizontalAlignment.CENTER} }; VerticalAlignment[][] verticalAlignments = new VerticalAlignment[][]{ new VerticalAlignment[]{VerticalAlignment.TOP, VerticalAlignment.TOP, VerticalAlignment.CENTER, VerticalAlignment.CENTER, null, null, null, null}, new VerticalAlignment[]{null, null, VerticalAlignment.BOTTOM, VerticalAlignment.BOTTOM, VerticalAlignment.BOTTOM, VerticalAlignment.BOTTOM, VerticalAlignment.BOTTOM, VerticalAlignment.BOTTOM} }; Boolean[][] wrapTexts = new Boolean[][]{ new Boolean[]{false, false, false, false, null, null, null, null}, new Boolean[]{null, null, false, false, true, true, false, true} }; int[][] headerMergedRegions = new int[][]{ new int[]{0, 0, 3, 7}, new int[]{0, 1, 0, 0}, new int[]{0, 1, 1, 1} }; // row headers String[] quarters = new String[]{"2016 Q1", "2016 Q2", "2016 Q3", "2016 Q4"}; String[] subcategories = new String[]{"Audio Equipment", "Cameras", "Computers", "Electronics - Miscellaneous", "TV's", "Video Equipment"}; // data Double[][] values = new Double[][]{ new Double[]{9579d, 8823d, .1942, 49320d,39456d}, new Double[]{11449d, 9619d, .2007, 57040d, 53047d}, new Double[]{4901d, 3784d, .2002, 24480d, 21298d}, new Double[]{12444d, 9525d, .1989, 62576d, 53815d}, new Double[]{8820d, 8059d, .2019, 43675d, 35814d}, new Double[]{16375d, 12986d, .2044, 80130d, 70514d}, new Double[]{8526d, 6929d, .1578, 54020d, 49698d}, new Double[]{11602d, 9578d, .1731, 67032d, 63680d}, new Double[]{4675d, 4380d, .1683, 27780d, 25752d}, new Double[]{11699d, 9421d, .1660, 70469d, 54966d}, new Double[]{9386d, 7179d, .1752, 53563d, 49974d}, new Double[]{10150d, 9213d, .1696, 59864d, 48490d}, new Double[]{8508d, 6772d, .1716, 49571d, 47092d}, new Double[]{16429d, 13529d, .1852, 88712d, 83389d}, new Double[]{6009d, 5391d, .1805, 33295d, 29200d}, new Double[]{11792d, 9791d, .1721, 68534d, 64285d}, new Double[]{9243d, 7952d, .1715, 53886d, 49953d}, new Double[]{14282d, 11679d, .1803, 79193d, 74441d}, new Double[]{10999d, 8538d, .1560, 70511d, 65575d}, new Double[]{14475d, 11433d, .1632, 88718d, 78515d}, new Double[]{5765d, 5029d, .1677, 34373d, 31847d}, new Double[]{11335d, 9567d, .1528, 74168d, 62672d}, new Double[]{11990d, 11230d, .1683, 71255d, 64414d}, new Double[]{13864d, 11328d, .1687, 82203d, 70695d} }; try (Workbook workbook = new XSSFWorkbook(); FileOutputStream fileout = new FileOutputStream("./Excel.xlsx") ) { // we need PropertyTemplate later PropertyTemplate propertyTemplate = new PropertyTemplate(); // we need properties map for cell styles later Map<String, Object> properties; // we need DataFormat later DataFormat format = workbook.createDataFormat(); // creating default font Font defaultFont = workbook.createFont(); defaultFont.setFontName("Calibri"); defaultFont.setFontHeightInPoints((short)12); // we need font in bold and white for headings Font defaultFontWhite = workbook.createFont(); defaultFontWhite.setFontName("Calibri"); defaultFontWhite.setFontHeightInPoints((short)12); defaultFontWhite.setBold(true); defaultFontWhite.setColor(IndexedColors.WHITE.getIndex()); // creating default cell style having default font CellStyle defaultCellStyle = workbook.createCellStyle(); defaultCellStyle.setFont(defaultFont); // we need currency style for numbers later CellStyle currency = workbook.createCellStyle(); currency.cloneStyleFrom(defaultCellStyle); currency.setDataFormat(format.getFormat("$#,##0")); // we need percent style for numbers later CellStyle percent = workbook.createCellStyle(); percent.cloneStyleFrom(defaultCellStyle); percent.setDataFormat(format.getFormat("0.00%")); Sheet sheet = workbook.createSheet(); // set column widths for (int c = 0; c < columnWidths.length; c++) sheet.setColumnWidth(c, columnWidths[c]); // set default column styles for (int c = 0; c < columnCellStyles.length; c++) { String style = columnCellStyles[c]; if ("default".equals(style)) { sheet.setDefaultColumnStyle(c, defaultCellStyle); } else if ("currency".equals(style)) { sheet.setDefaultColumnStyle(c, currency); } else if ("percent".equals(style)) { sheet.setDefaultColumnStyle(c, percent); } } int rowIdx = 0; // looping row index int colIdx = 0; // looping column index // input and format headers int i = 0; for (String[] headerRow : headers) { Row row = sheet.createRow(rowIdx++); colIdx = 0; int j = 0; for (String header : headerRow) { Cell cell = row.createCell(colIdx++); cell.setCellValue(header); properties = new HashMap<String, Object>(); properties.put(CellUtil.FILL_PATTERN, FillPatternType.SOLID_FOREGROUND); properties.put(CellUtil.FILL_FOREGROUND_COLOR, IndexedColors.BLUE.getIndex()); //do using only IndexedColors for fills properties.put(CellUtil.FONT, defaultFontWhite.getIndexAsInt()); // white font if (horizontalAlignments.length == headers.length) { HorizontalAlignment[] hAligns = horizontalAlignments[i]; if (hAligns.length == headerRow.length) { HorizontalAlignment hAlign = hAligns[j]; properties.put(CellUtil.ALIGNMENT, hAlign); // horizontal alignment } } if (verticalAlignments.length == headers.length) { VerticalAlignment[] hAligns = verticalAlignments[i]; if (hAligns.length == headerRow.length) { VerticalAlignment vAlign = hAligns[j]; properties.put(CellUtil.VERTICAL_ALIGNMENT, vAlign); // vertical alignment } } if (wrapTexts.length == headers.length) { Boolean[] isWrapTexts = wrapTexts[i]; if (isWrapTexts.length == headerRow.length) { Boolean isWrapText = isWrapTexts[j]; properties.put(CellUtil.WRAP_TEXT, isWrapText); // wrap text } } CellUtil.setCellStyleProperties(cell, properties); //do using CellUtil for **add** new properties to already applied cell styles j++; } i++; } // set header merged regions for (int[] mergedRegion : headerMergedRegions) { if (mergedRegion.length == 4) { sheet.addMergedRegion(new CellRangeAddress(mergedRegion[0], mergedRegion[1], mergedRegion[2], mergedRegion[3])); } } // draw header borders propertyTemplate.drawBorders(new CellRangeAddress(0, headers.length-1, 0, headers[0].length-1), BorderStyle.MEDIUM, IndexedColors.WHITE.getIndex(), BorderExtent.ALL); //since we have merged regions we can simply drawing all borders here // input and format row headers for (String quarter : quarters) { Row row = sheet.createRow(rowIdx++); Cell cell = row.createCell(0); // quarter in column 0 (A) cell.setCellValue(quarter); CellUtil.setVerticalAlignment(cell, VerticalAlignment.TOP); // quarter cells are top aligned for (String subcategory : subcategories) { cell = row.createCell(1); // subcategory in column 1 (B) cell.setCellValue(subcategory); sheet.addMergedRegion(new CellRangeAddress(rowIdx, rowIdx, 1, 2)); row = sheet.createRow(rowIdx++); } rowIdx--; sheet.addMergedRegion(new CellRangeAddress(rowIdx-subcategories.length, rowIdx-1, 0, 0)); } rowIdx -= quarters.length * subcategories.length; // reset the rowIdx // input values - number formats came from default column style for (Double[] rowValues : values) { Row row = sheet.getRow(rowIdx++); //get the row instead of creating it colIdx = 3; // values are from column 3 (D) on for (Double value : rowValues) { Cell cell = row.createCell(colIdx++); cell.setCellValue(value); cell.setCellStyle(getPreferredCellStyle(cell)); } } // draw data borders propertyTemplate.drawBorders(new CellRangeAddress(headers.length, headers.length+values.length-1, 0, headers[0].length-1), BorderStyle.MEDIUM, IndexedColors.LIGHT_ORANGE.getIndex(), BorderExtent.ALL); //since we have merged regions we can simply drawing all borders here //apply the PropertyTemplate borders propertyTemplate.applyBorders(sheet); workbook.write(fileout); } } }
How to set axis' labels in a Line Chart using Apache Poi
I'm trying to automate a report creation using java and Apache POI. I'm almost there, but can't find how to set the axis' labels in a XSSFChart. I already found how to to set the chart's title( Apache POI set Excel chart title ). Maybe there is a similar way to work it around, but I'm no developer and have no idea on how to start. Can anyone help? My code up to now: public void grafico(String nomeplanilhadados, String nomeplanilhagrafico, Date datainicial, Date datafinal, String[] nomesmarcos, String titulo){ if (datainicial.after(datafinal)){ throw new IllegalArgumentException("A data inicial precisa anteceder a data final"); } Sheet dados = this.wb.getSheet(nomeplanilhadados); Sheet planilhagrafico = this.wb.getSheet(nomeplanilhagrafico); Drawing drawing = planilhagrafico.createDrawingPatriarch(); ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, 4, 17, 20); Chart chart = drawing.createChart(anchor); ChartLegend legend = chart.getOrCreateLegend(); legend.setPosition(LegendPosition.TOP_RIGHT); LineChartData data = chart.getChartDataFactory().createLineChartData(); // Use a category axis for the bottom axis. ChartAxis bottomAxis = chart.getChartAxisFactory().createCategoryAxis(AxisPosition.BOTTOM); bottomAxis.setNumberFormat("MMM/yyyy"); ValueAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT); leftAxis.setCrosses(AxisCrosses.AUTO_ZERO); //retrieve the data int linhainicial=-1; int linhafinal=-1; Iterator<Row> rowIterator = dados.iterator(); while(rowIterator.hasNext()){ Row row = rowIterator.next(); Cell cell = row.getCell(0); if(cell!=null){ SimpleDateFormat formatodata = new SimpleDateFormat("dd-MM-yyyy"); Date date=cell.getDateCellValue(); if (linhainicial==-1 && date.compareTo(datainicial)>=0){ linhainicial=cell.getRowIndex(); } if( date.compareTo(datafinal)<=0){ linhafinal=cell.getRowIndex(); } } } ChartDataSource<Number> xs = DataSources.fromNumericCellRange(dados, new CellRangeAddress(linhainicial, linhafinal, 0, 0)); Row primeiralinha = dados.getRow(0); Iterator<Cell> cellIterator = primeiralinha.iterator(); while(cellIterator.hasNext()){ Cell cell=cellIterator.next(); if(cell!=null && Arrays.asList(nomesmarcos).contains(cell.getStringCellValue())){ ChartDataSource<Number> ys1 = DataSources.fromNumericCellRange(dados, new CellRangeAddress(linhainicial, linhafinal, cell.getColumnIndex(), cell.getColumnIndex())); //data.addSerie(xs, ys1); LineChartSerie chartSerie = data.addSerie(xs, ys1); chartSerie.setTitle(cell.getStringCellValue()); } } XSSFChart xchart = (XSSFChart) chart; CTChart ctChart = xchart.getCTChart(); CTTitle title = ctChart.addNewTitle(); CTTx tx = title.addNewTx(); CTTextBody rich = tx.addNewRich(); rich.addNewBodyPr(); // body properties must exist, but can be empty CTTextParagraph para = rich.addNewP(); CTRegularTextRun r = para.addNewR(); r.setT(titulo); chart.plot(data, bottomAxis, leftAxis); }
try use this method. public static void setAxisTitle(XSSFChart chart, int axisIdx, String title) { CTValAx valAx = chart.getCTChart().getPlotArea().getValAxArray(axisIdx); CTTitle ctTitle = valAx.addNewTitle(); ctTitle.addNewLayout(); ctTitle.addNewOverlay().setVal(false); CTTextBody rich = ctTitle.addNewTx().addNewRich(); rich.addNewBodyPr(); rich.addNewLstStyle(); CTTextParagraph p = rich.addNewP(); p.addNewPPr().addNewDefRPr(); p.addNewR().setT(title); p.addNewEndParaRPr(); }
I'm not sure if you got the answer yet. I'm not sure what your question is. I'm guessing that you want to set the axis labels so that your x-axis begins from the lowest/highest point. You can use this bottomAxis.setCrosses(AxisCrosses.MIN) or bottomAxis.setCrosses(AxisCrosses.MAX) You already used this in your code: bottomAxis.setCrosses(AxisCrosses.AUTO_ZERO)
Александр Кушнир's answer is a good way to add axis title,but the method he gave need some modification. //set title of category axis public static void setCatAxisTitle(XSSFChart chart, int axisIdx, String title) { CTCatAx valAx = chart.getCTChart().getPlotArea().getCatAxArray(axisIdx); CTTitle ctTitle = valAx.addNewTitle(); ctTitle.addNewLayout(); ctTitle.addNewOverlay().setVal(false); CTTextBody rich = ctTitle.addNewTx().addNewRich(); rich.addNewBodyPr(); rich.addNewLstStyle(); CTTextParagraph p = rich.addNewP(); p.addNewPPr().addNewDefRPr(); p.addNewR().setT(title); p.addNewEndParaRPr(); } public static void setValueAxisTitle(XSSFChart chart, int axisIdx, String title) { CTValAx valAx = chart.getCTChart().getPlotArea().getValAxArray(axisIdx); CTTitle ctTitle = valAx.addNewTitle(); ctTitle.addNewLayout(); ctTitle.addNewOverlay().setVal(false); CTTextBody rich = ctTitle.addNewTx().addNewRich(); rich.addNewBodyPr(); rich.addNewLstStyle(); CTTextParagraph p = rich.addNewP(); p.addNewPPr().addNewDefRPr(); p.addNewR().setT(title); p.addNewEndParaRPr(); } // Use a category axis for the bottom axis. ChartAxis bottomAxis =chart.getChartAxisFactory().createCategoryAxis(AxisPosition.BOTTOM); ValueAxis leftAxis =chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT); //But I didn't find a good way to get axisIdx.If there is only one chart in the plot area,you can set axis title like this. setValueAxisTitle(xchart,0,"title of bottom axis"); setCatAxisTitle(xchart,0, "title of left axis");
C# Example //set Value Axis private static void setValueAxisTitle(XSSFChart chart, int axisIdx, String title) { NPOI.OpenXmlFormats.Dml.Chart.CT_ValAx valAx = chart.GetCTChart().plotArea.valAx[axisIdx]; valAx.title = new NPOI.OpenXmlFormats.Dml.Chart.CT_Title(); NPOI.OpenXmlFormats.Dml.Chart.CT_Title ctTitle = valAx.title; ctTitle.layout = new NPOI.OpenXmlFormats.Dml.Chart.CT_Layout(); ctTitle.overlay = new NPOI.OpenXmlFormats.Dml.Chart.CT_Boolean(); ctTitle.overlay.val = 1; ctTitle.AddNewTx(); NPOI.OpenXmlFormats.Dml.Chart.CT_TextBody rich = ctTitle.tx.AddNewRich(); rich.AddNewBodyPr(); rich.AddNewLstStyle(); rich.AddNewP(); NPOI.OpenXmlFormats.Dml.CT_TextParagraph p = rich.p[0]; p.AddNewPPr(); p.pPr.defRPr = new NPOI.OpenXmlFormats.Dml.CT_TextCharacterProperties(); p.AddNewR().t=title; p.AddNewEndParaRPr(); } //set cat Axis private static void setCatAxisTitle(XSSFChart chart, int axisIdx, string title) { chart.GetCTChart().plotArea.catAx[axisIdx].title = new NPOI.OpenXmlFormats.Dml.Chart.CT_Title(); NPOI.OpenXmlFormats.Dml.Chart.CT_Title ctTitle = chart.GetCTChart().plotArea.catAx[axisIdx].title;// new NPOI.OpenXmlFormats.Dml.Chart.CT_Title(); ctTitle.layout = new NPOI.OpenXmlFormats.Dml.Chart.CT_Layout(); ctTitle.layout.AddNewManualLayout(); NPOI.OpenXmlFormats.Dml.Chart.CT_Boolean ctbool = new NPOI.OpenXmlFormats.Dml.Chart.CT_Boolean(); ctbool.val = 1; ctTitle.overlay = ctbool; ctTitle.AddNewTx(); NPOI.OpenXmlFormats.Dml.Chart.CT_TextBody rich = ctTitle.tx.AddNewRich(); rich.AddNewBodyPr(); rich.AddNewLstStyle(); rich.AddNewP(); NPOI.OpenXmlFormats.Dml.CT_TextParagraph p = rich.p[0]; p.AddNewPPr(); p.AddNewR().t = title; p.AddNewEndParaRPr(); }
JFreeChart XYSeries as Strings
im working with jfreechart and try to make a XYLineChart which is working very well. My problem is, that the y label are double values and i need strings. My Code: DefaultXYDataset result = new DefaultXYDataset(); XYSeries series1 = new XYSeries("Words"); series1.add(0, 0.3); series1.add(1, 0.5); series1.add(2, 0.6); series1.add(3, 0.3); series1.add(4, 0.2); series1.add(5, 1); result.addSeries(getTitle(), series1.toArray()); I want something like: XYSeries series1 = new XYSeries("Words"); series1.add("word 1", 0.3); series1.add("word 2", 0.5); ... The updated code using Symbol-Axis: private void test2() { XYDataset dataset = createDataset2(); JFreeChart chart = createChart2(dataset, "NN"); ChartPanel chartPanel = new ChartPanel(chart); chartPanel.setPreferredSize(new java.awt.Dimension(500, 250)); panel_visualize.add(chartPanel); } private DefaultXYDataset createDataset2() { DefaultXYDataset result = new DefaultXYDataset(); XYSeries series1 = new XYSeries("Words"); series1.add(0.3, 0); series1.add(0.5, 1); series1.add(0.6, 2); series1.add(0.3, 3); series1.add(0.2, 4); result.addSeries(getTitle(), series1.toArray()); return result; } private JFreeChart createChart2(XYDataset dataset, String title) { JFreeChart chart = ChartFactory.createXYLineChart(title, // chart title "Words", "Activation", dataset, // data PlotOrientation.HORIZONTAL, true, // include legend true, false); XYPlot plot = (XYPlot) chart.getPlot(); plot.setForegroundAlpha(0.5f); String[] grade = new String[5]; grade[0] = "Temp 0"; grade[1] = "Temp 1"; grade[2] = "Temp 2"; grade[3] = "Temp 3"; grade[4] = "Temp 4"; SymbolAxis rangeAxis = new SymbolAxis("Words", grade); rangeAxis.setTickUnit(new NumberTickUnit(1)); rangeAxis.setRange(0,grade.length); plot.setRangeAxis(rangeAxis); return chart; }
Using: plot.setDomainAxis(rangeAxis); solves my problem. Thanks to trashgod for the help.