/* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ package org.apache.poi.xssf.usermodel.charts; import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.DataFormat; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellReference; import org.apache.poi.util.LocaleUtil; 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.*; import org.apache.poi.xssf.usermodel.*; import org.junit.jupiter.api.Test; import org.openxmlformats.schemas.drawingml.x2006.chart.*; import java.io.IOException; import java.util.Calendar; /** * Tests for XSSFBubbleChartData. */ final class TestXSSFBubbleChartData { @Test void testExample() throws IOException { Object[][] chartData = new Object[][]{ new Object[]{"", "Category 1"}, new Object[]{"Bubble Size", "Bubble Date"}, new Object[]{1000, newCalendar(2020, 0, 1)}, new Object[]{10, newCalendar(2020, 0, 1)}, new Object[]{300, newCalendar(2021, 0, 1)}, new Object[]{"", ""}, new Object[]{"", "Category 2"}, new Object[]{"Bubble Size", "Bubble Date"}, new Object[]{100, newCalendar(2018, 0, 1)}, new Object[]{100, newCalendar(2020, 0, 1)} }; try (XSSFWorkbook wb = new XSSFWorkbook()) { XSSFSheet sheet = wb.createSheet("bubblechart"); DataFormat format = wb.createDataFormat(); XSSFCellStyle dateStyle = wb.createCellStyle(); dateStyle.setDataFormat(14); // 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()); if (value instanceof Calendar) { cell.setCellValue((Calendar) value); cell.setCellStyle(dateStyle); } colIndex++; } rowIndex++; } sheet.autoSizeColumn(0); sheet.autoSizeColumn(1); // create the chart // chart data sources XDDFDataSource xs1 = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(2, 4, 1, 1)); XDDFNumericalDataSource ys1 = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(2, 4, 0, 0)); XDDFNumericalDataSource bSz1 = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(2, 4, 0, 0)); XDDFDataSource xs2 = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(8, 9, 1, 1)); XDDFNumericalDataSource ys2 = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(8, 9, 0, 0)); XDDFNumericalDataSource bSz2 = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(8, 9, 0, 0)); // chart in drawing XSSFDrawing drawing = sheet.createDrawingPatriarch(); XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 3, 0, 15, 20); XSSFChart chart = drawing.createChart(anchor); chart.setTitleText("Chart Title"); chart.setTitleOverlay(false); chart.getFormattedTitle().getParagraph(0).addDefaultRunProperties().setFontSize(20d); // value axis x XDDFValueAxis valAxisX = chart.createValueAxis(AxisPosition.BOTTOM); valAxisX.setTitle("Axis Title"); // value axis y XDDFValueAxis valAxisY = chart.createValueAxis(AxisPosition.LEFT); valAxisY.setTitle("Axis Title"); // cross axes valAxisY.setCrosses(AxisCrosses.AUTO_ZERO); // chart data //XDDFChartData data = chart.createData(ChartTypes.???, valAxisX, valAxisY); XDDFBubbleChartData data = new XDDFBubbleChartData(chart, chart.getCTChart().getPlotArea().addNewBubbleChart(), valAxisX, valAxisY); // series XDDFBubbleChartData.Series series1 = (XDDFBubbleChartData.Series)data.addSeries(xs1, ys1); series1.setTitle("Category 1", new CellReference(sheet.getSheetName(), 0, 1, true, true)); // set bubble sizes series1.setBubbleSizes(bSz1); // add data labels // pos 8 = INT_R , showVal = true, showLegendKey= false, showCatName = true CTDLbls ctDLbls = setDataLabels(series1, 8, true, false, true); XDDFBubbleChartData.Series series2 = (XDDFBubbleChartData.Series)data.addSeries(xs2, ys2); series2.setTitle("Category 2", new CellReference(sheet.getSheetName(), 6, 1, true, true)); // set bubble sizes series2.setBubbleSizes(bSz2); // add data labels // pos 8 = INT_R , showVal = true, showLegendKey= false, showCatName = true ctDLbls = setDataLabels(series2, 8, true, false, true); // plot chart chart.plot(data); // legend XDDFChartLegend legend = chart.getOrAddLegend(); legend.setPosition(LegendPosition.RIGHT); // set series fill color solidFillSeries(data, 0, PresetColor.BLUE); solidFillSeries(data, 1, PresetColor.RED); // set rounded corners false setRoundedCorners(chart, false); // Write the output to a file try (UnsynchronizedByteArrayOutputStream outStream = UnsynchronizedByteArrayOutputStream.builder().get()) { wb.write(outStream); try (XSSFWorkbook wb2 = new XSSFWorkbook(outStream.toInputStream())) { // see if this fails } } } } private static Calendar newCalendar(int year, int month, int dayOfMonth) { return LocaleUtil.getLocaleCalendar(year, month, dayOfMonth); } 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 CTDLbls 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 */ CTDLbls ctDLbls = null; if (series instanceof XDDFBarChartData.Series) { CTBarSer ctBarSer = ((XDDFBarChartData.Series)series).getCTBarSer(); 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(STDLblPos.Enum.forInt(pos)); } else if (series instanceof XDDFLineChartData.Series) { CTLineSer ctLineSer = ((XDDFLineChartData.Series)series).getCTLineSer(); 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(STDLblPos.Enum.forInt(pos)); } else if (series instanceof XDDFPieChartData.Series) { CTPieSer ctPieSer = ((XDDFPieChartData.Series)series).getCTPieSer(); 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(STDLblPos.Enum.forInt(pos)); } else if (series instanceof XDDFBubbleChartData.Series) { CTBubbleSer ctBubbleSer = ((XDDFBubbleChartData.Series)series).getCTBubbleSer(); if (ctBubbleSer.isSetDLbls()) ctBubbleSer.unsetDLbls(); ctDLbls = ctBubbleSer.addNewDLbls(); if (!(pos == 3 || pos == 2 || pos == 6 || pos == 8 || pos == 9)) pos = 3; // bubble chart does not provide other pos ctDLbls.addNewDLblPos().setVal(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[8]:false); return ctDLbls; } return null; } private static void setRoundedCorners(XDDFChart chart, boolean setVal) { if (chart.getCTChartSpace().getRoundedCorners() == null) chart.getCTChartSpace().addNewRoundedCorners(); chart.getCTChartSpace().getRoundedCorners().setVal(setVal); } }