I have started working with Apache POI to create an Excel sheet that contains a chart. The chart is using data that is pretty simple, a date column and a numerical value column. I want to display the data in date reverse order so the newest data is at the top row in the spreadsheet, but I would like the chart to show the data in date normal order, because I'm also including a trend line, and the direction of the trend line is completely dependent on the ordering of the data. The date is being displayed on the x-axis, with the y-axis containing the numerical values. What I want to do is basically reverse all of the data on the chart. Is there some way to do this? In Google Sheets, it happens automatically, but that is not an option.
Here's the code I'm currently experimenting with:
public void addChart() {
final int NUM_OF_ROWS = ytd.size();
final int NUM_OF_COLUMNS = 2;
// Create a row and put some cells in it. Rows are 0 based.
XSSFRow row;
XSSFCell cell;
List<String> dates = new ArrayList<>();
List<Double> values = new ArrayList<>();
for (int rowIndex = 0; rowIndex < NUM_OF_ROWS; rowIndex++) {
row = (XSSFRow)sheet.createRow((short) rowIndex);
row.createCell(0).setCellValue(ytd.get(rowIndex).getShipDate());
dates.add(ytd.get(rowIndex).getShipDate());
for (int colIndex = 1; colIndex < NUM_OF_COLUMNS; colIndex++) {
cell = row.createCell((short) colIndex);
cell.setCellValue(new BigDecimal(ytd.get(rowIndex).getSquareFeet().replaceAll("[,]", "")).doubleValue());
values.add(new BigDecimal(ytd.get(rowIndex).getSquareFeet().replaceAll("[,]", "")).doubleValue());
}
}
Collections.reverse(dates);
Collections.reverse(values);
for (int rowIndex = 0; rowIndex < NUM_OF_ROWS; rowIndex++) {
row = sheet.getRow((short) rowIndex);
row.createCell(2).setCellValue(dates.get(rowIndex));
for (int colIndex = 3; colIndex < 4; colIndex++) {
cell = row.createCell((short) colIndex);
cell.setCellValue(values.get(rowIndex));
}
}
XSSFDrawing drawing = (XSSFDrawing) sheet.createDrawingPatriarch();
XSSFClientAnchor anchor = drawing.createAnchor(5, 5, 20, 20, 4, 4, 20, 25);
XSSFChart chart = drawing.createChart(anchor);
CTChart ctChart = ((XSSFChart) chart).getCTChart();
// XSSFChart chart = drawing.createChart(anchor);
XDDFChartLegend legend = chart.getOrAddLegend();
legend.setPosition(LegendPosition.TOP_RIGHT);
// Use a category axis for the bottom axis.
XDDFDateAxis bottomAxis = chart.createDateAxis(AxisPosition.BOTTOM);
bottomAxis.setTitle("Ship Date"); // https://stackoverflow.com/questions/32010765
XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
leftAxis.setTitle("Square Feet");
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
XDDFDataSource<Double> xs = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(0, NUM_OF_ROWS - 1, 2, 2));
XDDFNumericalDataSource<Double> ys1 = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(0, NUM_OF_ROWS-1, 3, 3));
XDDFLineChartData data = (XDDFLineChartData) chart.createData(ChartTypes.LINE, bottomAxis, leftAxis);
XDDFLineChartData.Series series1 = (XDDFLineChartData.Series) data.addSeries(xs, ys1);
series1.setTitle("SqFt", null); // https://stackoverflow.com/questions/21855842
series1.setSmooth(false); // https://stackoverflow.com/questions/29014848
series1.setMarkerStyle(MarkerStyle.DOT); // https://stackoverflow.com/questions/39636138
chart.plot(data);
// sheet.setColumnHidden(2, true);
// sheet.setColumnHidden(3, true);
// 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);
chart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0)
.addNewTrendline()
.addNewTrendlineType()
.setVal(org.openxmlformats.schemas.drawingml.x2006.chart.STTrendlineType.LINEAR);
// Write the output to a file
try (FileOutputStream fileOut = new FileOutputStream("ooxml-line-chart.xlsx")) {
workbook.write(fileOut);
} catch (FileNotFoundException ex) {
Exceptions.printStackTrace(ex);
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
I've tried putting the data in columns that are hidden, which works fine in LibreOffice, but doesn't work in excel until I un-hide the columns. So does anyone know if there is a way to control the ordering of the graph data, separate from the data in the table?
I found the solution: you can change the direction of the data by modifying the category axis on the chart, using the enum AxisOrientation. A side effect of getting the data to chart in the order I want, is that the labels for the y-axis now appear on the right instead of the left, but I think I can live with that.
Related
I need to change the first and the last bars. As you can see, both bars are halfed due the limits of the chart.
Which class from Apache POI librar should I use to change that?
This is the code which is drawing the chart
private ByteArrayInputStream generateFileDetails(List<ClientDataRequest> clientDataRequest, String metricName){
ByteArrayOutputStream file = new ByteArrayOutputStream();
try{
try(XSSFWorkbook workbook = new XSSFWorkbook()){
XSSFSheet sheet = workbook.createSheet(metricName);
Font headerFont = workbook.createFont();
headerFont.setBold(true);
headerFont.setUnderline(Font.U_SINGLE);
CellStyle headerCellStyle = workbook.createCellStyle();
headerCellStyle.setFillBackgroundColor(IndexedColors.GREY_40_PERCENT.getIndex());
headerCellStyle.setFont(headerFont);
XSSFDrawing drawing = sheet.createDrawingPatriarch();
XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 5, 1, 14, 10+ clientDataRequest.size());
Row headerRow = sheet.createRow(anchor.getRow2()+1);
headerRow.createCell(0).setCellValue("Client Name");
headerRow.createCell(1).setCellValue("Value");
for (int i = 0; i < headerRow.getLastCellNum(); i++) {
headerRow.getCell(i).setCellStyle(headerCellStyle);
}
for (int i = 0; i < clientDataRequest.size(); i++) {
Row row = sheet.createRow(anchor.getRow2()+2+i);
row.createCell(0).setCellValue(clientDataRequest.get(i).getClientName());
sheet.autoSizeColumn(0);
row.createCell(1).setCellValue(clientDataRequest.get(i).getValue());
sheet.autoSizeColumn(1);
}
XSSFChart chart = drawing.createChart(anchor);
chart.setTitleText(metricName);
chart.setTitleOverlay(false);
//XDDFChartLegend legend = chart.getOrAddLegend();
//legend.setPosition(LegendPosition.TOP_RIGHT);
XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
bottomAxis.setTitle("Client Name");
XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
leftAxis.setTitle("Value");
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
XDDFDataSource<String> clientNames = XDDFDataSourcesFactory.fromStringCellRange(sheet,
new CellRangeAddress(anchor.getRow2()+2, anchor.getRow2()+1+clientDataRequest.size(), 0, 0));
XDDFNumericalDataSource<Double> values = XDDFDataSourcesFactory.fromNumericCellRange(sheet,
new CellRangeAddress(anchor.getRow2()+2, anchor.getRow2()+1+clientDataRequest.size(), 1, 1));
XDDFChartData data = chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
XDDFChartData.Series series = data.addSeries(clientNames, values);
XDDFLineProperties line = new XDDFLineProperties();
series.setTitle(null, null);
data.setVaryColors(false);
chart.plot(data);
XDDFBarChartData bar = (XDDFBarChartData) data;
bar.setBarDirection(BarDirection.BAR);
workbook.write(file);
}
} catch (Exception e){
}
return new ByteArrayInputStream(file.toByteArray());
}
It does not have many documentation and samples about the Apache POI.
For a bar chart you additional need set AxisCrossBetween for the value axis, so the value 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.
In your case:
...
XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
leftAxis.setTitle("Value");
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
leftAxis.setCrossBetween(org.apache.poi.xddf.usermodel.chart.AxisCrossBetween.BETWEEN);
...
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());
...
After building a XSSFScatterChartData and I filling it using the method XSSFChart.plot(ChartData data, ChartAxis... chartAxis), the plot contains markers but linked by a line..
I think the problem comes from the method XSSFScatterChartData.addStyle which sets a STScatterStyle.LINE_MARKER by default.
Here is a copy of the method I use to generate the chart:
private void setTrainingTimeGraph(Sheet trainingTimeSheet, Sheet resultsSheet) {
Drawing drawing = trainingTimeSheet.createDrawingPatriarch();
ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 5, 5, 20, 30);
XSSFChart chart = (XSSFChart) drawing.createChart(anchor);
ChartLegend legend = chart.getOrCreateLegend();
legend.setPosition(LegendPosition.TOP_RIGHT);
chart.setTitleText("Training time over Fscore");
XSSFScatterChartData data = chart.getChartDataFactory().createScatterChartData();
ValueAxis bottomAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.BOTTOM);
ValueAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT);
setValueAxisTitle((XSSFChart) chart,0,"Fscore");
setValueAxisTitle((XSSFChart) chart,1, "Training Time");
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
bottomAxis.setCrosses(AxisCrosses.AUTO_ZERO);
ChartDataSource<Number> xs = DataSources.fromNumericCellRange(resultsSheet, new CellRangeAddress(16, 29, 10, 10));
ChartDataSource<Number> ys = DataSources.fromNumericCellRange(resultsSheet, new CellRangeAddress(16, 29, 18, 18));
data.addSerie(xs, ys);
chart.plot(data,bottomAxis, leftAxis);
}
UPDATE
So adding #AxelRichter code to set to no fill in my scatter chart data serie:
...
data.addSerie(xs, ys);
chart.plot(data,bottomAxis, leftAxis);
//set line properties of first scatter chart data serie to no fill:
((XSSFChart)chart).getCTChart().getPlotArea().getScatterChartArray(0).getSerArray(0)
.addNewSpPr().addNewLn().addNewNoFill();
I manged to get rid of the lines linking the Markers.. Finally!
But the second part wasn't what I was looking for. Let me explain it a little bit better:
When I pass hover each point in my scatterPlot there is some text which pops up ("label/x_value", x_value,y_value). The value in the legend is the same as its "label/x_value". I would like to set for each data point and for each value in legend its "label/x_value".
Thanks in advance!
Excel scatter charts will always have line between the markers too. If you don't wants to see this line it's fill color must be set to no fill.
References:
CTScatterSer -> CTShapeProperties -> CTLineProperties -> CTLineProperties.addNewNoFill
And for having data labels the series needs a CTDLbl setting.
Complete example:
import java.io.FileOutputStream;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.usermodel.charts.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFChart;
import org.apache.poi.ss.util.CellRangeAddress;
public class CreateExcelScatterChart {
public static void main(String[] args) throws Exception {
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet("chart");
final int NUM_OF_ROWS = 2;
final int NUM_OF_COLUMNS = 20;
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(Math.sin(Math.PI*colIndex/10*2));
}
}
Drawing<?> drawing = sheet.createDrawingPatriarch();
ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 3, 13, 20);
Chart chart = drawing.createChart(anchor);
ChartLegend legend = chart.getOrCreateLegend();
legend.setPosition(LegendPosition.TOP_RIGHT);
ScatterChartData data = chart.getChartDataFactory().createScatterChartData();
ValueAxis bottomAxis = chart.getChartAxisFactory().createValueAxis(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));
ScatterChartSeries chartSerie = data.addSerie(xs, ys1);
chartSerie.setTitle("My Title");
chart.plot(data, bottomAxis, leftAxis);
//set line properties of first scatter chart data serie to no fill:
((XSSFChart)chart).getCTChart().getPlotArea().getScatterChartArray(0).getSerArray(0)
.addNewSpPr().addNewLn().addNewNoFill();
//set data labels for first scatter chart data serie
((XSSFChart)chart).getCTChart().getPlotArea().getScatterChartArray(0).getSerArray(0)
.addNewDLbls()
.addNewNumFmt().setFormatCode("0.0");
((XSSFChart)chart).getCTChart().getPlotArea().getScatterChartArray(0).getSerArray(0)
.getDLbls().getNumFmt()
.setSourceLinked(false);
((XSSFChart)chart).getCTChart().getPlotArea().getScatterChartArray(0).getSerArray(0)
.getDLbls()
.addNewShowLegendKey().setVal(false);
((XSSFChart)chart).getCTChart().getPlotArea().getScatterChartArray(0).getSerArray(0)
.getDLbls()
.addNewShowCatName().setVal(false);
((XSSFChart)chart).getCTChart().getPlotArea().getScatterChartArray(0).getSerArray(0)
.getDLbls()
.addNewShowSerName().setVal(false);
((XSSFChart)chart).getCTChart().getPlotArea().getScatterChartArray(0).getSerArray(0)
.getDLbls()
.addNewShowPercent().setVal(false);
((XSSFChart)chart).getCTChart().getPlotArea().getScatterChartArray(0).getSerArray(0)
.getDLbls()
.addNewShowBubbleSize().setVal(false);
((XSSFChart)chart).getCTChart().getPlotArea().getScatterChartArray(0).getSerArray(0)
.getDLbls()
.addNewShowVal().setVal(true);
wb.write(new FileOutputStream("CreateExcelScatterChart.xlsx"));
wb.close();
}
}
This code needs the full jar of all of the schemas ooxml-schemas-1.3.jar as mentioned in FAQ-N10025.
This code works using the current last stable release apache poi version 3.17. Note: The Chart code of apache poi is highly in development now. So the code may not work in further releases.
So basing myself on the anwser of #Axel Richter.
Here is the to code used as basis:
import java.io.FileOutputStream;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.usermodel.charts.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFChart;
import org.apache.poi.ss.util.CellRangeAddress;
public class CreateExcelScatterChart {
public static void main(String[] args) throws Exception {
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet("chart");
final int NUM_OF_ROWS = 2;
final int NUM_OF_COLUMNS = 20;
Row row;
Cell cell;
//x values
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));
}
}
// y values
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(Math.sin(Math.PI*colIndex/10*2));
}
}
Drawing<?> drawing = sheet.createDrawingPatriarch();
ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 3, 13, 20);
Chart chart = drawing.createChart(anchor);
ChartLegend legend = chart.getOrCreateLegend();
legend.setPosition(LegendPosition.TOP_RIGHT);
ScatterChartData data = chart.getChartDataFactory().createScatterChartData();
ValueAxis bottomAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.BOTTOM);
ValueAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT);
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
bottomAxis.setCrosses(AxisCrosses.AUTO_ZERO);
for (int i = 0; i < NUM_OF_COLUMNS; i++) {
ChartDataSource<Number> xs = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(0, 0, i, i));
ChartDataSource<Number> ys = DataSources.fromNumericCellRange(sheet, new CellRangeAddress(1, 1, i, i));
ScatterChartSeries chartSerie = data.addSerie(xs, ys);
chartSerie.setTitle("My Title " + i);
}
chart.plot(data, bottomAxis, leftAxis);
//set line properties of first scatter chart data serie to no fill:
CTScatterSer[] scatterChartSeries = ((XSSFChart) chart).getCTChart().getPlotArea().getScatterChartArray(0).getSerArray();
for (int i = 0; i < scatterChartSeries.length; i++) {
scatterChartSeries[i].addNewSpPr().addNewLn().addNewNoFill();
}
wb.write(new FileOutputStream("CreateExcelScatterChart.xlsx"));
wb.close();
}
}
It will generate 20 series each one with a title when passing hover and the same title in the legend. There will be as no lines between each marker. So basically it does what I was looking for.
Thank you for your answers.
Ps: In case that you want to use some labels on each marker follow the //set data labels for first scatter chart data serie on #Richet answer
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);
...