Search code examples
javachartsapache-poixlsx

ScatterChart using LineMarker as ScatterStyle instead of only Marker


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!


Solution

  • 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