I am creating an Excel file with line chart. I've created chart and filled it with data but I cannot create points on my chart. Does anyone know, is there a way I could generate these points(triangles, squares, circles etc.) in a chart using apache poi?
This is my code for generating current char:
public static void main(String[] args) throws Exception {
Workbook wb = new XSSFWorkbook();
Sheet dataSheet = wb.createSheet("linechart");
final int NUM_OF_ROWS = 10;
final int NUM_OF_COLUMNS = 4;
Row row;
Cell cell;
for (int rowIndex = 0; rowIndex < NUM_OF_ROWS; rowIndex++) {
row = dataSheet.createRow((short) rowIndex);
for (int colIndex = 0; colIndex < NUM_OF_COLUMNS; colIndex++) {
cell = row.createCell((short) colIndex);
cell.setCellValue(rowIndex * ((colIndex + 1) + ((int) (Math.random() * 10))));
}
}
Drawing drawing = dataSheet.createDrawingPatriarch();
ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, NUM_OF_COLUMNS + 2, 3, NUM_OF_COLUMNS + 15, 20);
Chart chart = drawing.createChart(anchor);
ChartLegend legend = chart.getOrCreateLegend();
legend.setPosition(LegendPosition.RIGHT);
LineChartData data = chart.getChartDataFactory().createLineChartData();
ChartAxis bottomAxis = chart.getChartAxisFactory().createCategoryAxis(AxisPosition.BOTTOM);
ValueAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT);
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
ChartDataSource<Number> xs = DataSources.fromNumericCellRange(dataSheet, new CellRangeAddress(0, NUM_OF_ROWS - 1, 0, 0));
ChartDataSource<Number> ys1 = DataSources.fromNumericCellRange(dataSheet, new CellRangeAddress(0, NUM_OF_ROWS - 1, 1, 1));
ChartDataSource<Number> ys2 = DataSources.fromNumericCellRange(dataSheet, new CellRangeAddress(0, NUM_OF_ROWS - 1, 2, 2));
ChartDataSource<Number> ys3 = DataSources.fromNumericCellRange(dataSheet, new CellRangeAddress(0, NUM_OF_ROWS - 1, 3, 3));
LineChartSeries series1 = data.addSeries(xs, ys1);
series1.setTitle("one");
LineChartSeries series2 = data.addSeries(xs, ys2);
series2.setTitle("two");
LineChartSeries series3 = data.addSeries(xs, ys3);
series3.setTitle("three");
chart.plot(data, bottomAxis, leftAxis);
XSSFChart xssfChart = (XSSFChart) chart;
CTPlotArea plotArea = xssfChart.getCTChart().getPlotArea();
plotArea.getLineChartArray()[0].getSmooth();
CTBoolean ctBool = CTBoolean.Factory.newInstance();
ctBool.setVal(false);
plotArea.getLineChartArray()[0].setSmooth(ctBool);
for (CTLineSer ser : plotArea.getLineChartArray()[0].getSerArray()) {
ser.setSmooth(ctBool);
}
FileOutputStream fileOut = new FileOutputStream("chart.xlsx");
wb.write(fileOut);
fileOut.close();
}
Solution:
CTPlotArea plotArea = xssfChart.getCTChart().getPlotArea();
CTMarker ctMarker = CTMarker.Factory.newInstance();
ctMarker.setSymbol(CTMarkerStyle.Factory.newInstance());
for (CTLineSer ser : plotArea.getLineChartArray()[0].getSerArray()) {
ser.setMarker(ctMarker);
}
Following code will give you what exactly you are looking for
CTPlotArea plotArea = xssfChart.getCTChart().getPlotArea();
plotArea.getLineChartArray()[0].getSmooth();
CTMarker ctMarker = CTMarker.Factory.newInstance();
ctMarker.setSymbol(CTMarkerStyle.Factory.newInstance());
CTBoolean ctBool = CTBoolean.Factory.newInstance();
ctBool.setVal(false);
plotArea.getLineChartArray()[0].setSmooth(ctBool);
for (CTLineSer ser : plotArea.getLineChartArray()[0].getSerArray()) {
ser.setSmooth(ctBool);
ser.setMarker(ctMarker);
}
Related
I'm trying to get a different ending from a line chart witch apache Poi.
This is my Code. It's creates an this Chart without the arrow.
I couldn't find a Axis "Style Class" or something like this.
XSSFDrawing drawing = sheet.createDrawingPatriarch();
XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 5, 10, 15);
XSSFChart chart = drawing.createChart(anchor);
setRoundedCorners(chart, false);
XDDFChartLegend legend = chart.getOrAddLegend();
legend.setPosition(LegendPosition.TOP_RIGHT);
// Use a category axis for the bottom axis.
XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
bottomAxis.setTitle("x"); // https://stackoverflow.com/questions/32010765
//XDDFValueAxis valueAxis = chart.createValueAxis(AxisPosition.BOTTOM);
//bottomAxis.setMajorTickMark(AxisTickMark.CROSS);
XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
leftAxis.setTitle("f(x)");
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
I added the arrow after the creation. Does anyone now how I can create the arrow inside the Code?
I changed the Axis class to XDDFValueAxis and found something that looks like my mussing feature but unfortunately throw the line 'lineProperties.getHeadEnd()' an null pointer exception.
XDDFValueAxis valueAxis = chart.createValueAxis(AxisPosition.BOTTOM);
XDDFShapeProperties shapeProperties = valueAxis.getOrAddShapeProperties();
XDDFLineProperties lineProperties = shapeProperties.getLineProperties();
XDDFLineEndProperties lineEndProperties = lineProperties.getHeadEnd();
lineEndProperties.setType(LineEndType.ARROW);
valueAxis.setTitle("x");
How can I create XDDFLineEndProperties the constructor is protected?
Here is the full Code Example if needed.
mport 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.XDDFLineProperties;
import org.apache.poi.xddf.usermodel.XDDFShapeProperties;
import org.apache.poi.xddf.usermodel.XDDFSolidFillProperties;
import org.apache.poi.xddf.usermodel.chart.*;
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;
/**
* Line chart example.
*/
public final class LineChart {
private LineChart() {}
public static void main(String[] args) throws IOException {
try (XSSFWorkbook wb = new XSSFWorkbook()) {
XSSFSheet sheet = wb.createSheet("linechart");
final int NUM_OF_ROWS = 3;
final int NUM_OF_COLUMNS = 10;
// Create a row and put some cells in it. Rows are 0 based.
Row row;
Cell cell;
for (int rowIndex = 0; rowIndex < NUM_OF_ROWS; rowIndex++) {
row = sheet.createRow((short) rowIndex);
for (int colIndex = 0; colIndex < NUM_OF_COLUMNS; colIndex++) {
cell = row.createCell((short) colIndex);
cell.setCellValue(colIndex * (rowIndex + 1.0) -5 );
}
}
XSSFDrawing drawing = sheet.createDrawingPatriarch();
XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 5, 10, 15);
XSSFChart chart = drawing.createChart(anchor);
setRoundedCorners(chart, false);
XDDFChartLegend legend = chart.getOrAddLegend();
legend.setPosition(LegendPosition.TOP_RIGHT);
// Use a category axis for the bottom axis.
XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
bottomAxis.setTitle("x"); // https://stackoverflow.com/questions/32010765
//XDDFValueAxis valueAxis = chart.createValueAxis(AxisPosition.BOTTOM);
//bottomAxis.setMajorTickMark(AxisTickMark.CROSS);
XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
leftAxis.setTitle("f(x)");
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
XDDFDataSource<Double> xs = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(0, 0, 0, NUM_OF_COLUMNS - 1));
XDDFNumericalDataSource<Double> ys1 = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(1, 1, 0, NUM_OF_COLUMNS - 1));
XDDFNumericalDataSource<Double> ys2 = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(2, 2, 0, NUM_OF_COLUMNS - 1));
XDDFLineChartData data = (XDDFLineChartData) chart.createData(ChartTypes.LINE, bottomAxis, leftAxis);
XDDFLineChartData.Series series1 = (XDDFLineChartData.Series) data.addSeries(xs, ys1);
series1.setTitle("2x", null); // https://stackoverflow.com/questions/21855842
series1.setSmooth(false); // https://stackoverflow.com/questions/29014848
series1.setMarkerStyle(MarkerStyle.STAR); // https://stackoverflow.com/questions/39636138
XDDFLineChartData.Series series2 = (XDDFLineChartData.Series) data.addSeries(xs, ys2);
series2.setTitle("3x", null);
series2.setSmooth(true);
series2.setMarkerSize((short) 6);
series2.setMarkerStyle(MarkerStyle.TRIANGLE); // https://stackoverflow.com/questions/39636138
chart.plot(data);
// if your series have missing values like https://stackoverflow.com/questions/29014848
// chart.displayBlanksAs(DisplayBlanks.GAP);
// https://stackoverflow.com/questions/24676460
//solidLineSeries(data, 0, PresetColor.CHARTREUSE);
//solidLineSeries(data, 1, PresetColor.TURQUOISE);
// Write the output to a file
try (FileOutputStream fileOut = new FileOutputStream("ooxml-line-chart.xlsx")) {
wb.write(fileOut);
}
}
}
private static void setRoundedCorners(XSSFChart chart, boolean setVal) {
if (chart.getCTChartSpace().getRoundedCorners() == null) chart.getCTChartSpace().addNewRoundedCorners();
chart.getCTChartSpace().getRoundedCorners().setVal(setVal);
}
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);
}
}
You could have a public class which extends XDDFLineEndProperties and provides a not protected constructor:
import org.apache.poi.xddf.usermodel.XDDFLineEndProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTLineEndProperties;
public class MyXDDFLineEndProperties extends XDDFLineEndProperties {
public MyXDDFLineEndProperties() {
super(CTLineEndProperties.Factory.newInstance());
}
}
Then you could use that like this:
...
// Use a category axis for the bottom axis.
XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
bottomAxis.setTitle("x"); // https://stackoverflow.com/questions/32010765
XDDFShapeProperties shapeProperties = bottomAxis.getOrAddShapeProperties();
XDDFLineProperties lineProperties = new XDDFLineProperties();
MyXDDFLineEndProperties lineEndProperties = new MyXDDFLineEndProperties();
lineEndProperties.setType(LineEndType.ARROW);
lineProperties.setTailEnd(lineEndProperties);
shapeProperties.setLineProperties(lineProperties);
...
I use poi version is 4.1.1.
Now I want to make first column doesn't display on y-axis, means I want to add some space between y-axis and the first column.
I notice there is an option "Axis position" on x-axis, when it set to "Between tick marks", it will work as I want.
But I don't know how to make it using apache-poi. Is it possible to make it?
here is the "Asis position in excel"
now the line chart looks like
what I want
Thanks everyone!!!
Here is the code example from Apache website:
public class LineChart {
public static void main(String[] args) throws IOException {
try (XSSFWorkbook wb = new XSSFWorkbook()) {
XSSFSheet sheet = wb.createSheet("linechart");
final int NUM_OF_ROWS = 3;
final int NUM_OF_COLUMNS = 10;
// Create a row and put some cells in it. Rows are 0 based.
Row row;
Cell cell;
for (int rowIndex = 0; rowIndex < NUM_OF_ROWS; rowIndex++) {
row = sheet.createRow((short) rowIndex);
for (int colIndex = 0; colIndex < NUM_OF_COLUMNS; colIndex++) {
cell = row.createCell((short) colIndex);
cell.setCellValue(colIndex * (rowIndex + 1.0));
}
}
XSSFDrawing drawing = sheet.createDrawingPatriarch();
XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 5, 10, 15);
XSSFChart chart = drawing.createChart(anchor);
XDDFChartLegend legend = chart.getOrAddLegend();
legend.setPosition(LegendPosition.BOTTOM);
// Use a category axis for the bottom axis.
XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
//bottomAxis.setTitle("x"); // https://stackoverflow.com/questions/32010765
XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
//leftAxis.setTitle("f(x)");
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
XDDFDataSource<Double> xs = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(0, 0, 0, NUM_OF_COLUMNS - 1));
XDDFNumericalDataSource<Double> ys1 = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(1, 1, 0, NUM_OF_COLUMNS - 1));
XDDFNumericalDataSource<Double> ys2 = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(2, 2, 0, NUM_OF_COLUMNS - 1));
XDDFLineChartData data = (XDDFLineChartData) chart.createData(ChartTypes.LINE, bottomAxis, leftAxis);
XDDFLineChartData.Series series1 = (XDDFLineChartData.Series) data.addSeries(xs, ys1);
series1.setTitle("a", null); // https://stackoverflow.com/questions/21855842
//series1.setSmooth(false); // https://stackoverflow.com/questions/29014848
series1.setMarkerStyle(MarkerStyle.NONE); // https://stackoverflow.com/questions/39636138
XDDFLineChartData.Series series2 = (XDDFLineChartData.Series) data.addSeries(xs, ys2);
series2.setTitle("b", null);
//series2.setSmooth(true);
//series2.setMarkerSize((short) 6);
series2.setMarkerStyle(MarkerStyle.NONE); // https://stackoverflow.com/questions/39636138
CTBoolean ctBoolean = CTBoolean.Factory.newInstance();
ctBoolean.setVal(false);
CTPlotArea plotArea = chart.getCTChart().getPlotArea();
chart.getCTChartSpace().setRoundedCorners(ctBoolean);
for (CTLineSer ser : plotArea.getLineChartArray()[0].getSerArray()) {
CTDLbls ctdLbls = ser.addNewDLbls();
ctdLbls.setShowSerName(ctBoolean);
ctdLbls.setShowLegendKey(ctBoolean);
ctdLbls.setShowLeaderLines(ctBoolean);
ctdLbls.setShowCatName(ctBoolean);
CTDLblPos ctdLblPos = CTDLblPos.Factory.newInstance();
ctdLblPos.setVal(STDLblPos.CTR);
CTDLblPos.Factory.newInstance();
ctdLbls.setDLblPos(ctdLblPos);
}
chart.plot(data);
// if your series have missing values like https://stackoverflow.com/questions/29014848
// chart.displayBlanksAs(DisplayBlanks.GAP);
// https://stackoverflow.com/questions/24676460
solidLineSeries(data, 0, PresetColor.LIGHT_GREEN);
solidLineSeries(data, 1, PresetColor.DARK_RED);
// Write the output to a file
try (FileOutputStream fileOut = new FileOutputStream("ooxml-line-chart.xlsx")) {
wb.write(fileOut);
}
}
}
//CTPresetLineDashProperties
private static void solidLineSeries(XDDFChartData data, int index, PresetColor color) {
XDDFSolidFillProperties fill = new XDDFSolidFillProperties(XDDFColor.from(color));
XDDFLineProperties line = new XDDFLineProperties();
if (index == 0) {
line.setPresetDash(new XDDFPresetLineDash(PresetLineDash.DOT));
}
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);
}
}
Per default the value axis and the category axis crosses exactly on category point. So if value axis crosses category axis at point 0, then it looks like your result now.
But if x axis is a category axis, then there is a setting XDDFValueAxis.setCrossBetween. If that is set to AxisCrossBetween.BETWEEN then the value axis crosses the category axis between the category points. This looks as you want.
So in your case:
...
// Use a category axis for the bottom axis.
XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
//bottomAxis.setTitle("x"); // https://stackoverflow.com/questions/32010765
XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
//leftAxis.setTitle("f(x)");
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
leftAxis.setCrossBetween(AxisCrossBetween.BETWEEN);
...
Btw.: You should get rid of deprecated methods.
For example the get...Array() methods of ooxml-schemas are deprecated. Either you get a concrete item from the array using get...Array(item) or you get a List using get...List().
In your case:
...
//for (CTLineSer ser : plotArea.getLineChartArray()[0].getSerArray()) {
for (CTLineSer ser : plotArea.getLineChartArray(0).getSerList()) {
...
And also XDDFChartData.getSeries() is deprecated. If you need one series do using XDDFChartData.getSeries(index)
In your case:
...
//XDDFChartData.Series series = data.getSeries().get(index);
XDDFChartData.Series series = data.getSeries(index);
...
The scatter chart example included with Apache POI shows how to set the color of a line (that connects markers in a series), but I cannot figure out how to set the color of a marker for a series. I see that I can change the marker icon (with setMarkerStyle (javadoc), but that does not appear to have editable color properties).
From a previous SO question I suspect that it will be necessary to disable the vary colors configuration, but I still do not know how to set the colors after that step (if the following line is even necessary).
((XSSFChart)chart).getCTChart().getPlotArea().getScatterChartArray(0)
.addNewVaryColors().setVal(false);
How can I specify the marker icon colors in the example below?
public class ScatterChart {
public static void main(String[] args) throws IOException {
try (XSSFWorkbook wb = new XSSFWorkbook()) {
XSSFSheet sheet = wb.createSheet("Sheet 1");
final int NUM_OF_ROWS = 3;
final int NUM_OF_COLUMNS = 10;
// Create a row and put some cells in it. Rows are 0 based.
Row row;
Cell cell;
for (int rowIndex = 0; rowIndex < NUM_OF_ROWS; rowIndex++) {
row = sheet.createRow((short) rowIndex);
for (int colIndex = 0; colIndex < NUM_OF_COLUMNS; colIndex++) {
cell = row.createCell((short) colIndex);
cell.setCellValue(colIndex * (rowIndex + 1.0));
}
}
XSSFDrawing drawing = sheet.createDrawingPatriarch();
XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 5, 10, 15);
XSSFChart chart = drawing.createChart(anchor);
XDDFChartLegend legend = chart.getOrAddLegend();
legend.setPosition(LegendPosition.TOP_RIGHT);
XDDFValueAxis bottomAxis = chart.createValueAxis(AxisPosition.BOTTOM);
bottomAxis.setTitle("x"); // https://stackoverflow.com/questions/32010765
XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
leftAxis.setTitle("f(x)");
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
XDDFDataSource<Double> xs = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(0, 0, 0, NUM_OF_COLUMNS - 1));
XDDFNumericalDataSource<Double> ys1 = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(1, 1, 0, NUM_OF_COLUMNS - 1));
XDDFNumericalDataSource<Double> ys2 = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(2, 2, 0, NUM_OF_COLUMNS - 1));
XDDFScatterChartData data = (XDDFScatterChartData) chart.createData(ChartTypes.SCATTER, bottomAxis, leftAxis);
XDDFScatterChartData.Series series1 = (XDDFScatterChartData.Series) data.addSeries(xs, ys1);
series1.setTitle("2x", null); // https://stackoverflow.com/questions/21855842
series1.setSmooth(false); // https://stackoverflow.com/questions/39636138
XDDFScatterChartData.Series series2 = (XDDFScatterChartData.Series) data.addSeries(xs, ys2);
series2.setTitle("3x", null);
chart.plot(data);
solidLineSeries(data, 0, PresetColor.CHARTREUSE);
solidLineSeries(data, 1, PresetColor.TURQUOISE);
// Write the output to a file
try (FileOutputStream fileOut = new FileOutputStream("ooxml-scatter-chart.xlsx")) {
wb.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 previous question you linked is from May 2018 and is about the pure XSSFChart stuff from apache poi upto version 3.17. This is outdated since apache poi 4.0.0 introduced the new XDDF stuff.
Nevertheless setting the marker color is not supported until now using only the high level XDDF classes. The marker has shape properties having fill properties of the same kind as the series itself. So we can use XDDFSolidFillProperties and XDDFShapeProperties as for the line settings. But to get the marker, we need using the low level underlying ooxml-schemas-1.4 beans.
Example:
...
series2.setMarkerStyle(MarkerStyle.DIAMOND);
series2.setMarkerSize((short)15);
XDDFSolidFillProperties fillMarker = new XDDFSolidFillProperties(XDDFColor.from(new byte[]{(byte)0xFF, (byte)0xFF, 0x00}));
XDDFShapeProperties propertiesMarker = new XDDFShapeProperties();
propertiesMarker.setFillProperties(fillMarker);
chart.getCTChart().getPlotArea().getScatterChartArray(0).getSerArray(1).getMarker()
.addNewSpPr().set(propertiesMarker.getXmlObject());
...
I've generated a line chart using Apache POI. I need to change the default colors I got in the chart. Can I use RGB codes to define specific colors to each line?
My code is as follows.
Drawing drawing = sheet4.createDrawingPatriarch();
ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, 1, 17, 22);
Chart chart = drawing.createChart(anchor);
ChartLegend legend = chart.getOrCreateLegend();
legend.setPosition(LegendPosition.RIGHT);
LineChartData data = chart.getChartDataFactory().createLineChartData();
ChartAxis bottomAxis = chart.getChartAxisFactory().createCategoryAxis(AxisPosition.BOTTOM);
ValueAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT);
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
ChartDataSource<Number> xs = DataSources.fromNumericCellRange(sheet1, new CellRangeAddress(1, 380, 0, 0));
ChartDataSource<Number> ys1 = DataSources.fromNumericCellRange(sheet1, new CellRangeAddress(1, 380, 1, 1));
ChartDataSource<Number> ys2 = DataSources.fromNumericCellRange(sheet1, new CellRangeAddress(1, 380, 3, 3));
ChartDataSource<Number> ys3 = DataSources.fromNumericCellRange(sheet1, new CellRangeAddress(1, 380, 4, 4));
ChartDataSource<Number> ys4 = DataSources.fromNumericCellRange(sheet1, new CellRangeAddress(1, 380, 8, 8));
LineChartSeries series1 = data.addSeries(xs, ys1);
series1.setTitle("Value 1");
LineChartSeries series2 = data.addSeries(xs, ys2);
series2.setTitle("Value 2");
LineChartSeries series3 = data.addSeries(xs, ys3);
series3.setTitle("Value 3");
LineChartSeries series4 = data.addSeries(xs, ys4);
series4.setTitle("Value 4");
chart.plot(data, bottomAxis, leftAxis);
XSSFChart xssfChart = (XSSFChart) chart;
CTPlotArea plotArea = xssfChart.getCTChart().getPlotArea();
plotArea.getLineChartArray()[0].getSmooth();
CTBoolean ctBool = CTBoolean.Factory.newInstance();
ctBool.setVal(false);
plotArea.getLineChartArray()[0].setSmooth(ctBool);
for (CTLineSer ser : plotArea.getLineChartArray()[0].getSerArray()) {
ser.setSmooth(ctBool);
}
I am always a friend of complete examples. Then others can reproducing this and don't need guess how the Excel sheet might look like.
The following complete example does creating a new Excel workbook in Office Open XML format (*.xlsx) having a sheet containing some example data and the line chart.
First code part creates a line chart how apache poi version 3.17 creates it per default.
Then in second code part, I do customizing the chart. The code parts are commented to show what they shall do. After that customizing the chart also is OpenOffice / Libreoffice Calc compatible. The apache poi default charts are not OpenOffice / Libreoffice Calc compatible. At least up to version 3.17.
For customizing one needs knowledge about the internal low level objects apache poi uses as basic objects. Those are contained in ooxml-schemas-1.3.jar . Unfortunately there is not more a API doc public available. So one must download ooxml-schemas-1.3-sources.jar and then do javadoc one's own to get a documentation.
Also one should know that files in Office Open XML format (*.xlsx) simply are ZIP archives. So one can unzip them and have a look at /xl/charts/chart1.xml. When one does this after apache poi has created the chart and do comparing that XML with the XML after re-saving after done the wanted changings in Excel, then one will see the needed XML changings.
Example:
import java.io.FileOutputStream;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.usermodel.charts.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.util.Units;
public class CreateExcelLineChartAndCustomize {
public static void main(String[] args) throws Exception {
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet("linechart");
final int NUM_OF_ROWS = 100;
final int NUM_OF_COLUMNS = 5;
// create some data
Row row;
Cell cell;
String[] headings = new String []{"x", "sin(x)", "cos(x)", "random", "random/x"};
for (int rowIndex = 0; rowIndex < 1; rowIndex++) {
row = sheet.createRow(rowIndex);
for (int colIndex = 0; colIndex < NUM_OF_COLUMNS; colIndex++) {
cell = row.createCell(colIndex);
cell.setCellValue(headings[colIndex]);
}
}
for (int rowIndex = 1; rowIndex < NUM_OF_ROWS; rowIndex++) {
row = sheet.createRow(rowIndex);
for (int colIndex = 0; colIndex < NUM_OF_COLUMNS; colIndex++) {
cell = row.createCell(colIndex);
switch (colIndex) {
case 0:
cell.setCellValue(rowIndex/10d);
break;
case 1:
cell.setCellFormula("SIN(A" + (rowIndex+1) + ")");
break;
case 2:
cell.setCellFormula("COS(A" + (rowIndex+1) + ")");
break;
case 3:
cell.setCellFormula("RAND()");
break;
case 4:
cell.setCellFormula("-D"+(rowIndex+1));
break;
default:
}
}
}
// create default chart
Drawing<?> drawing = sheet.createDrawingPatriarch();
ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 6, 1, 15, 21);
Chart chart = drawing.createChart(anchor);
ChartLegend legend = chart.getOrCreateLegend();
legend.setPosition(LegendPosition.RIGHT);
if (chart instanceof XSSFChart) ((XSSFChart)chart).setTitleText("Some lines");
LineChartData data = chart.getChartDataFactory().createLineChartData();
ChartAxis bottomAxis = chart.getChartAxisFactory().createCategoryAxis(AxisPosition.BOTTOM);
ValueAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT);
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
ChartDataSource<Number> xs = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(1, 99, 0, 0));
ChartDataSource<Number> ys1 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(1, 99, 1, 1));
ChartDataSource<Number> ys2 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(1, 99, 2, 2));
ChartDataSource<Number> ys3 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(1, 99, 3, 3));
ChartDataSource<Number> ys4 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(1, 99, 4, 4));
LineChartSeries series1 = data.addSeries(xs, ys1);
series1.setTitle("Value 1");
LineChartSeries series2 = data.addSeries(xs, ys2);
series2.setTitle("Value 2");
LineChartSeries series3 = data.addSeries(xs, ys3);
series3.setTitle("Value 3");
LineChartSeries series4 = data.addSeries(xs, ys4);
series4.setTitle("Value 4");
chart.plot(data, bottomAxis, leftAxis);
// customize the chart, this also makes it OpenOffice/Libreoffice Calc compatible
if (chart instanceof XSSFChart) {
XSSFChart xssfChart = (XSSFChart)chart;
// do not auto delete the title
if (xssfChart.getCTChart().getAutoTitleDeleted() == null) xssfChart.getCTChart().addNewAutoTitleDeleted();
xssfChart.getCTChart().getAutoTitleDeleted().setVal(false);
// plot area background and border line
if (xssfChart.getCTChartSpace().getSpPr() == null) xssfChart.getCTChartSpace().addNewSpPr();
if (xssfChart.getCTChartSpace().getSpPr().getSolidFill() == null)
xssfChart.getCTChartSpace().getSpPr().addNewSolidFill();
if (xssfChart.getCTChartSpace().getSpPr().getSolidFill().getSrgbClr() == null)
xssfChart.getCTChartSpace().getSpPr().getSolidFill().addNewSrgbClr();
xssfChart.getCTChartSpace().getSpPr().getSolidFill().getSrgbClr().setVal(new byte[]{(byte)255,(byte)255,(byte)255});
if (xssfChart.getCTChartSpace().getSpPr().getLn() == null) xssfChart.getCTChartSpace().getSpPr().addNewLn();
xssfChart.getCTChartSpace().getSpPr().getLn().setW(Units.pixelToEMU(1));
if (xssfChart.getCTChartSpace().getSpPr().getLn().getSolidFill() == null)
xssfChart.getCTChartSpace().getSpPr().getLn().addNewSolidFill();
if (xssfChart.getCTChartSpace().getSpPr().getLn().getSolidFill().getSrgbClr() == null)
xssfChart.getCTChartSpace().getSpPr().getLn().getSolidFill().addNewSrgbClr();
xssfChart.getCTChartSpace().getSpPr().getLn().getSolidFill().getSrgbClr().setVal(new byte[]{(byte)0,(byte)0,(byte)0});
// line style of cat axis
if (xssfChart.getCTChart().getPlotArea().getCatAxArray(0).getSpPr() == null)
xssfChart.getCTChart().getPlotArea().getCatAxArray(0).addNewSpPr();
if (xssfChart.getCTChart().getPlotArea().getCatAxArray(0).getSpPr().getLn() == null)
xssfChart.getCTChart().getPlotArea().getCatAxArray(0).getSpPr().addNewLn();
xssfChart.getCTChart().getPlotArea().getCatAxArray(0).getSpPr().getLn().setW(Units.pixelToEMU(1));
if (xssfChart.getCTChart().getPlotArea().getCatAxArray(0).getSpPr().getLn().getSolidFill() == null)
xssfChart.getCTChart().getPlotArea().getCatAxArray(0).getSpPr().getLn().addNewSolidFill();
if (xssfChart.getCTChart().getPlotArea().getCatAxArray(0).getSpPr().getLn().getSolidFill().getSrgbClr() == null)
xssfChart.getCTChart().getPlotArea().getCatAxArray(0).getSpPr().getLn().getSolidFill().addNewSrgbClr();
xssfChart.getCTChart().getPlotArea().getCatAxArray(0).getSpPr().getLn().getSolidFill().getSrgbClr()
.setVal(new byte[]{(byte)0,(byte)0,(byte)0});
//line style of val axis
if (xssfChart.getCTChart().getPlotArea().getValAxArray(0).getSpPr() == null)
xssfChart.getCTChart().getPlotArea().getValAxArray(0).addNewSpPr();
if (xssfChart.getCTChart().getPlotArea().getValAxArray(0).getSpPr().getLn() == null)
xssfChart.getCTChart().getPlotArea().getValAxArray(0).getSpPr().addNewLn();
xssfChart.getCTChart().getPlotArea().getValAxArray(0).getSpPr().getLn().setW(Units.pixelToEMU(1));
if (xssfChart.getCTChart().getPlotArea().getValAxArray(0).getSpPr().getLn().getSolidFill() == null)
xssfChart.getCTChart().getPlotArea().getValAxArray(0).getSpPr().getLn().addNewSolidFill();
if (xssfChart.getCTChart().getPlotArea().getValAxArray(0).getSpPr().getLn().getSolidFill().getSrgbClr() == null)
xssfChart.getCTChart().getPlotArea().getValAxArray(0).getSpPr().getLn().getSolidFill().addNewSrgbClr();
xssfChart.getCTChart().getPlotArea().getValAxArray(0).getSpPr().getLn().getSolidFill().getSrgbClr()
.setVal(new byte[]{(byte)0,(byte)0,(byte)0});
// line style of the series
for (int i = 0; i < 4; i++) {
if (xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(i).getSpPr() == null)
xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(i).addNewSpPr();
if (xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(i).getSpPr().getLn() == null)
xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(i).getSpPr().addNewLn();
xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(i)
.getSpPr().getLn().setW(Units.pixelToEMU(3));
if (xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(i).getSpPr().getLn().getSolidFill() == null)
xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(i).getSpPr().getLn().addNewSolidFill();
if (xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(i).getSpPr().getLn().getSolidFill().getSrgbClr() == null)
xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(i).getSpPr().getLn().getSolidFill().addNewSrgbClr();
}
// first series red
xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0)
.getSpPr().getLn().getSolidFill().getSrgbClr().setVal(new byte[]{(byte)255,(byte)0,(byte)0});
// second series green
xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(1)
.getSpPr().getLn().getSolidFill().getSrgbClr().setVal(new byte[]{(byte)0,(byte)255,(byte)0});
// third series blue
xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(2)
.getSpPr().getLn().getSolidFill().getSrgbClr().setVal(new byte[]{(byte)0,(byte)0,(byte)255});
// fourth series yellow
xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(3)
.getSpPr().getLn().getSolidFill().getSrgbClr().setVal(new byte[]{(byte)255,(byte)255,(byte)0});
// set line data series to not smooth the line
for (int i = 0; i < 4; i++) {
if (xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(i).getSmooth() == null)
xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(i).addNewSmooth();
xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(i).getSmooth().setVal(false);
}
// set or unset tick marks
for (int i = 0; i < 4; i++) {
if (xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(i).getMarker() == null)
xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(i).addNewMarker();
if (xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(i).getMarker().getSymbol() == null)
xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(i).getMarker().addNewSymbol();
}
// no tick marks
xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0).getMarker().getSymbol().setVal(
org.openxmlformats.schemas.drawingml.x2006.chart.STMarkerStyle.NONE);
// diamond tick marks
xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(1).getMarker().getSymbol().setVal(
org.openxmlformats.schemas.drawingml.x2006.chart.STMarkerStyle.DIAMOND);
// auto tick marks = marker set but no symbol given
xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(2).getMarker().unsetSymbol();
// no tick marks
xssfChart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(3).getMarker().getSymbol().setVal(
org.openxmlformats.schemas.drawingml.x2006.chart.STMarkerStyle.NONE);
}
wb.write(new FileOutputStream("CreateExcelLineChartAndCustomize.xlsx"));
wb.close();
}
}
Result:
Apache POI 4
private void lineSeriesColor(XDDFChartData.Series series, XDDFColor color) {
XDDFSolidFillProperties fill = new XDDFSolidFillProperties(color);
XDDFLineProperties line = new XDDFLineProperties();
line.setFillProperties(fill);
XDDFShapeProperties properties = series.getShapeProperties();
if (properties == null) {
properties = new XDDFShapeProperties();
}
properties.setLineProperties(line);
series.setShapeProperties(properties);
}
private byte[] hex2Rgb(String colorStr) {
int r = Integer.valueOf(colorStr.substring(1, 3), 16);
int g = Integer.valueOf(colorStr.substring(3, 5), 16);
int b = Integer.valueOf(colorStr.substring(5, 7), 16);
return new byte[]{(byte) r, (byte) g, (byte) b};
}
then
String color = "#0C1111";
lineSeriesColor(series, XDDFColor.from(hex2Rgb(color)));
I want to make line chart in Excel with apache poi. I want to cut or make 0 those parts of graph, which are negative. I have this example from apache poi examples. Is there any solution to this?
My code.
try (Workbook wb = new XSSFWorkbook()) {
Sheet sheet = wb.createSheet("linechart");
final int NUM_OF_ROWS = 2;
final int NUM_OF_COLUMNS = 10;
// Create a row and put some cells in it. Rows are 0 based.
Row row;
Cell cell;
for (int rowIndex = 0; rowIndex < 1; rowIndex++) {
row = sheet.createRow((short) rowIndex);
for (int colIndex = 0; colIndex < NUM_OF_COLUMNS; colIndex++) {
cell = row.createCell((short) colIndex);
cell.setCellValue(4*colIndex * (rowIndex + 1));
}
}
for (int rowIndex = 1; rowIndex < NUM_OF_ROWS; rowIndex++) {
row = sheet.createRow((short) rowIndex);
for (int colIndex = 0; colIndex < NUM_OF_COLUMNS; colIndex++) {
cell = row.createCell((short) colIndex);
cell.setCellValue(ThreadLocalRandom.current().nextInt(3));
}
}
Drawing<?> drawing = sheet.createDrawingPatriarch();
ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 0, 20, 15);
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);
ValueAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT);
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
bottomAxis.setCrosses(AxisCrosses.AUTO_ZERO);
ChartDataSource<Number> xs = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(0, 0, 0, NUM_OF_COLUMNS - 1));
ChartDataSource<Number> ys1 = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(1, 1, 0, NUM_OF_COLUMNS - 1));
LineChartSeries chartSerie = data.addSeries(xs, ys1);
chartSerie.setTitle("My Title");
chart.plot(data, bottomAxis, leftAxis);
// Write the output to a file
try (FileOutputStream fileOut = new FileOutputStream("ooxml-line-chart.xlsx")) {
wb.write(fileOut);
}
}
This is what I have now:
my line chart
this is what i want
As one can find in apache poi's API docs ValueAxis extends ChartAxis and ChartAxis has a method ChartAxis.setMinimum.
So
...
ValueAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT);
leftAxis.setMinimum(0);
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
...