word poi-tl 图表功能增强,插入图表折线图、柱状图、饼状图

目录

    • 问题
      • 解决问题
      • poi-tl介绍
    • 功能实现
      • 引入依赖
      • 功能介绍
    • 功能实例
      • 饼图
        • 模版
        • 代码
        • 效果图
      • 雷达图(模版同饼图)
        • 代码
        • 效果图
      • 柱状图(模版同饼图)
        • 代码
        • 效果图
    • 附加
      • CustomCharts 工具类
      • CustomChartSingleSeriesRenderData 数据对象
      • CustomChartRenderPolicy 插件类

问题

由于在开发功能需求中,word文档需要根据数据动态生成图表,不同的数据类型生成不同的图表信息,而word模版引擎原有功能只能做替换,不满足需求;

解决问题

  • 目前选择的poi-tl的模版引擎,在原有的基础上新增自定义插件来实现功能

poi-tl介绍

poi-tl 是一个基于Apache POI的Word模板引擎,也是一个免费开源的Java类库,你可以非常方便的加入到你的项目中;

Word模板引擎功能描述
文本将标签渲染为文本
图片将标签渲染为图片
表格将标签渲染为表格
图表条形图(3D条形图)、柱形图(3D柱形图)、面积图(3D面积图)、折线图(3D折线图)、雷达图、饼图(3D饼图)、散点图等图表渲染
If Condition判断根据条件隐藏或者显示某些文档内容(包括文本、段落、图片、表格、列表、图表等)
Foreach Loop循环根据集合循环某些文档内容(包括文本、段落、图片、表格、列表、图表等)
Loop表格行循环复制渲染表格的某一行
Loop表格列循环复制渲染表格的某一列
Loop有序列表支持有序列表的循环,同时支持多级列表
Highlight代码高亮word中代码块高亮展示,支持26种语言和上百种着色样式
Markdown将Markdown渲染为word文档
Word批注完整的批注功能,创建批注、修改批注等
Word附件Word中插入附件
SDT内容控件内容控件内标签支持
Textbox文本框文本框内标签支持
图片替换将原有图片替换成另一张图片
书签、锚点、超链接支持设置书签,文档内锚点和超链接功能
Expression Language完全支持SpringEL表达式,可以扩展更多的表达式:OGNL, MVEL
样式支持有序列表的循环,同时支持多级列表
模板嵌套模板包含子模板,子模板再包含子模板
模板嵌套模板包含子模板,子模板再包含子模板
合并Word合并Merge,也可以在指定位置进行合并
用户自定义函数(插件)插件化设计,在文档任何位置执行函数

功能实现

引入依赖

		<dependency>
            <groupId>com.deepoove</groupId>
            <artifactId>poi-tl</artifactId>
            <version>1.12.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml-full</artifactId>
            <version>5.2.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.2.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>5.2.5</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.25</version>
        </dependency>
        <!-- spring el表达式 -->
        <dependency>
	  		<groupId>org.springframework</groupId>
		  	<artifactId>spring-expression</artifactId>
		 	 <version>5.3.18</version>
		</dependency>

功能介绍

  • 目前支持的图表类型有饼图、柱形图、面积图、折线图、雷达图等
  • 同时支持添加到表格一起渲染

功能实例

饼图

模版

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/53b227ba4eca4923871b3f526f6784dd.png

代码
    @Test
    public void test() throws Exception {
        Configure config = Configure.builder()
                .addPlugin('&',new CustomChartRenderPolicy())
                .useSpringEL(false).build();
        Map<String,Object> dataMap = new HashMap<String, Object>();
        CustomChartSingleSeriesRenderData chartSingleSeriesRenderData = CustomCharts.ofPie("日期", new String[]{"2024-01", "2024-02", "2024-03", "2024-04", "2024-05", "2024-06",
                        "2024-07", "2024-08", "2024-09", "2024-10", "2024-11", "2024-12"})
                .series("数值", new Integer[]{10, 35, 21, 46, 79, 55,
                        39, 32, 71, 28, 22, 11}).setWidthAndHeight(10,10).create();
        dataMap.put("testChars", chartSingleSeriesRenderData);


        ClassPathResource classPathResource = new ClassPathResource("static/word/template.docx");
        try (InputStream resourceInputStream = classPathResource.getInputStream();
             XWPFTemplate template = XWPFTemplate.compile(resourceInputStream,config);){

            template.render(dataMap);
            template.writeAndClose(new FileOutputStream("output.docx"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
效果图

在这里插入图片描述

雷达图(模版同饼图)

代码
 @Test
    public void test() throws Exception {
        Configure config = Configure.builder()
                .addPlugin('&',new CustomChartRenderPolicy())
                .useSpringEL(false).build();
        Map<String,Object> dataMap = new HashMap<String, Object>();
        CustomChartSingleSeriesRenderData chartSingleSeriesRenderData = CustomCharts.ofRadar("日期", new String[]{"2024-01", "2024-02", "2024-03", "2024-04", "2024-05", "2024-06",
                        "2024-07", "2024-08", "2024-09", "2024-10", "2024-11", "2024-12"})
                .series("数值", new Integer[]{10, 35, 21, 46, 79, 55,
                        39, 32, 71, 28, 22, 11}).setWidthAndHeight(10,10).create();
        dataMap.put("testChars", chartSingleSeriesRenderData);


        ClassPathResource classPathResource = new ClassPathResource("static/word/template.docx");
        try (InputStream resourceInputStream = classPathResource.getInputStream();
             XWPFTemplate template = XWPFTemplate.compile(resourceInputStream,config);){

            template.render(dataMap);
            template.writeAndClose(new FileOutputStream("output.docx"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
效果图

在这里插入图片描述

柱状图(模版同饼图)

代码
 @Test
    public void test() throws Exception {
        Configure config = Configure.builder()
                .addPlugin('&',new CustomChartRenderPolicy())
                .useSpringEL(false).build();
        Map<String,Object> dataMap = new HashMap<String, Object>();
        CustomChartSingleSeriesRenderData chartSingleSeriesRenderData = CustomCharts.ofBar("日期", new String[]{"2024-01", "2024-02", "2024-03", "2024-04", "2024-05", "2024-06",
                        "2024-07", "2024-08", "2024-09", "2024-10", "2024-11", "2024-12"})
                .series("数值", new Integer[]{10, 35, 21, 46, 79, 55,
                        39, 32, 71, 28, 22, 11}).setWidthAndHeight(10,10).create();
        dataMap.put("testChars", chartSingleSeriesRenderData);


        ClassPathResource classPathResource = new ClassPathResource("static/word/template.docx");
        try (InputStream resourceInputStream = classPathResource.getInputStream();
             XWPFTemplate template = XWPFTemplate.compile(resourceInputStream,config);){

            template.render(dataMap);
            template.writeAndClose(new FileOutputStream("output.docx"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
效果图

在这里插入图片描述

附加

CustomCharts 工具类


import com.deepoove.poi.data.RenderData;
import com.deepoove.poi.data.RenderDataBuilder;
import com.deepoove.poi.data.SeriesRenderData;
import org.apache.poi.xddf.usermodel.chart.ChartTypes;

public class CustomCharts {

    public static CustomCharts.ChartSingles ofArea(String chartTitle, String[] categories) {
        return ofSingleSeries(chartTitle, categories, ChartTypes.AREA);
    }

    public static CustomCharts.ChartSingles ofRadar(String chartTitle, String[] categories) {
        return ofSingleSeries(chartTitle, categories, ChartTypes.RADAR);
    }

    public static CustomCharts.ChartSingles ofLine(String chartTitle, String[] categories) {
        return ofSingleSeries(chartTitle, categories, ChartTypes.LINE);
    }

    public static CustomCharts.ChartSingles ofBar(String chartTitle, String[] categories) {
        return ofSingleSeries(chartTitle, categories, ChartTypes.BAR);
    }

    public static CustomCharts.ChartSingles ofPie(String chartTitle, String[] categories) {
        return ofSingleSeries(chartTitle, categories, ChartTypes.PIE);
    }

    public static CustomCharts.ChartSingles ofPie3D(String chartTitle, String[] categories) {
        return ofSingleSeries(chartTitle, categories, ChartTypes.PIE3D);
    }

    public static CustomCharts.ChartSingles ofDoughnut(String chartTitle, String[] categories) {
        return ofSingleSeries(chartTitle, categories, ChartTypes.DOUGHNUT);
    }

    public static CustomCharts.ChartSingles ofSingleSeries(String chartTitle, String[] categories, ChartTypes chartTypes) {
        return new CustomCharts.ChartSingles(chartTitle, categories, chartTypes);
    }

    public static interface ChartSetting<T extends RenderData> {
        CustomCharts.ChartBuilder<T> setxAsixTitle(String xAxisTitle);

        CustomCharts.ChartBuilder<T> setyAsixTitle(String yAxisTitle);
    }

    public static abstract class ChartBuilder<T extends RenderData> implements RenderDataBuilder<T>, CustomCharts.ChartSetting<T> {
        protected String chartTitle;
        protected String xAxisTitle;
        protected String yAxisTitle;
        protected String[] categories;
        protected ChartTypes chartTypes;

        protected ChartBuilder(String chartTitle, String[] categories, ChartTypes chartTypes) {
            this.chartTitle = chartTitle;
            this.categories = categories;
            this.chartTypes = chartTypes;
        }

        protected void checkLengh(int length) {
            if (categories.length != length) {
                throw new IllegalArgumentException(
                        "The length of categories and series values in chart must be the same!");
            }
        }

        public CustomCharts.ChartBuilder<T> setxAsixTitle(String xAxisTitle) {
            this.xAxisTitle = xAxisTitle;
            return this;
        }

        public CustomCharts.ChartBuilder<T> setyAsixTitle(String yAxisTitle) {
            this.yAxisTitle = yAxisTitle;
            return this;
        }
    }


    /**
     * builder to build single series chart
     */
    public static class ChartSingles extends CustomCharts.ChartBuilder<CustomChartSingleSeriesRenderData> {
        private SeriesRenderData series;
        /**
         * 宽度
         */
        private Integer width = 10;

        /**
         * 高度
         */
        private Integer height = 6;

        private ChartSingles(String chartTitle, String[] categories, ChartTypes chartTypes) {
            super(chartTitle, categories, chartTypes);
        }

        public CustomCharts.ChartSingles series(String name, Number[] value) {
            checkLengh(value.length);
            series = new SeriesRenderData(name, value);
            return this;
        }

        public CustomCharts.ChartSingles setWidthAndHeight(Integer width, Integer height) {
            this.width = width;
            this.height = height;
            return this;
        }

        @Override
        public CustomChartSingleSeriesRenderData create() {
            CustomChartSingleSeriesRenderData data = new CustomChartSingleSeriesRenderData();
            data.setChartTitle(chartTitle);
            data.setxAxisTitle(xAxisTitle);
            data.setyAxisTitle(yAxisTitle);
            data.setCategories(categories);
            data.setSeriesData(series);
            data.setChartTypes(chartTypes);
            data.setWidth(width);
            data.setHeight(height);
            return data;
        }
    }
}

CustomChartSingleSeriesRenderData 数据对象

import com.deepoove.poi.data.ChartSingleSeriesRenderData;
import lombok.Data;
import org.apache.poi.xddf.usermodel.chart.ChartTypes;

@Data
public class CustomChartSingleSeriesRenderData extends ChartSingleSeriesRenderData {

    private ChartTypes chartTypes;

    /**
     * 宽度
     */
    private Integer width;

    /**
     * 高度
     */
    private Integer height;
}

CustomChartRenderPolicy 插件类



import cn.hutool.core.util.StrUtil;
import com.deepoove.poi.data.SeriesRenderData;
import com.deepoove.poi.policy.AbstractRenderPolicy;
import com.deepoove.poi.render.RenderContext;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.util.Units;
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.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.MarkerStyle;
import org.apache.poi.xddf.usermodel.chart.RadarStyle;
import org.apache.poi.xddf.usermodel.chart.XDDFBarChartData;
import org.apache.poi.xddf.usermodel.chart.XDDFCategoryAxis;
import org.apache.poi.xddf.usermodel.chart.XDDFCategoryDataSource;
import org.apache.poi.xddf.usermodel.chart.XDDFChartAxis;
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.XDDFLineChartData;
import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource;
import org.apache.poi.xddf.usermodel.chart.XDDFPieChartData;
import org.apache.poi.xddf.usermodel.chart.XDDFRadarChartData;
import org.apache.poi.xddf.usermodel.chart.XDDFValueAxis;
import org.apache.poi.xwpf.usermodel.XWPFChart;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTPieSer;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class CustomChartRenderPolicy extends AbstractRenderPolicy<CustomChartSingleSeriesRenderData> {

    private Boolean titleOverlayCode;

    public CustomChartRenderPolicy() {
        this(false);
    }

    public CustomChartRenderPolicy(Boolean titleOverlayCode) {
        this.titleOverlayCode = titleOverlayCode;
    }

    @Override
    protected void afterRender(RenderContext<CustomChartSingleSeriesRenderData> context) {
        //清空标签 clearParagraph 为true 存在表外的图表渲染不了
        clearPlaceholder(context, false);
    }

    @Override
    public void doRender(RenderContext<CustomChartSingleSeriesRenderData> context) throws Exception {
        XWPFRun run = context.getRun();
        XWPFDocument xwpfDocument = (XWPFDocument)context.getXWPFDocument();
        CustomChartSingleSeriesRenderData singleSeriesRenderData = context.getData();
        if (Objects.isNull(singleSeriesRenderData)) {
            return;
        }

        Integer height = singleSeriesRenderData.getHeight();
        Integer width = singleSeriesRenderData.getWidth();
        //在标签位置创建chart图表对象
        XWPFChart chart = xwpfDocument.createChart(run, width * Units.EMU_PER_CENTIMETER, height * Units.EMU_PER_CENTIMETER);

        SeriesRenderData seriesData = singleSeriesRenderData.getSeriesData();

        //图例是否覆盖标题
        chart.setTitleOverlay(this.titleOverlayCode);
        String[] xAxisData = singleSeriesRenderData.getCategories();

        Number[] yAxisData = seriesData.getValues();
        ChartTypes chartTypes = singleSeriesRenderData.getChartTypes();
        //创建图表对象
        execChartData(chart, chartTypes, singleSeriesRenderData, xAxisData, yAxisData);

        //图表相关设置
        //图表标题
        if (StrUtil.isNotEmpty(singleSeriesRenderData.getChartTitle())) {
            chart.setTitleText(singleSeriesRenderData.getChartTitle());
        } else {
            chart.removeTitle();
            chart.deleteLegend();
        }
    }

    private static void solidFillSeries(XDDFChartData.Series series, PresetColor color) {
        XDDFSolidFillProperties fill = new XDDFSolidFillProperties(XDDFColor.from(color));
        XDDFShapeProperties properties = series.getShapeProperties();
        if (properties == null) {
            properties = new XDDFShapeProperties();
        }
        properties.setFillProperties(fill);
        series.setShapeProperties(properties);
    }

    private void execChartData(XWPFChart chart, ChartTypes chartType, CustomChartSingleSeriesRenderData singleSeriesRenderData
            , String[] xAxisData, Number[] yAxisData) {
        XDDFChartData xddfChartData = null;
        switch (chartType) {
            case AREA:

                break;
            case AREA3D:

                break;
            case BAR:
                xddfChartData = performBarRendering(chart, chartType, singleSeriesRenderData, xAxisData, yAxisData);
                break;
            case BAR3D:

                break;
            case DOUGHNUT:
                break;
            case LINE:
                xddfChartData = performLineRendering(chart, chartType, singleSeriesRenderData, xAxisData, yAxisData);
                break;
            case LINE3D:
                break;
            case PIE:
                xddfChartData = performPieRendering(chart, chartType, singleSeriesRenderData, xAxisData, yAxisData);
                break;
            case PIE3D:
                break;
            case RADAR:
                performRadarRendering(chart, chartType, singleSeriesRenderData, xAxisData, yAxisData);
                break;
            case SCATTER:
                break;
            case SURFACE:
                break;
            case SURFACE3D:
                break;
            default:
                break;
        }

        //在标签位置绘制折线图
        if (Objects.nonNull(xddfChartData)) {
            chart.plot(xddfChartData);
        }
    }

    /**
     * PIE
     *
     * @param chart
     * @param chartType
     * @param xAxisData
     * @param yAxisData
     * @return
     */
    private XDDFChartData performPieRendering(XWPFChart chart, ChartTypes chartType, CustomChartSingleSeriesRenderData singleSeriesRenderData
            , String[] xAxisData, Number[] yAxisData) {
        // 图例位置
        XDDFChartLegend legend = chart.getOrAddLegend();
        legend.setPosition(LegendPosition.TOP_RIGHT);
        //设置X轴数据
        XDDFCategoryDataSource xAxisSource = XDDFDataSourcesFactory.fromArray(xAxisData);
        //设置Y轴数据
        XDDFNumericalDataSource<Number> yAxisSource = XDDFDataSourcesFactory.fromArray(yAxisData);
        //创建对象
        // 将数据源绑定到饼图上
        XDDFChartData xddfPieChartData = chart.createData(ChartTypes.PIE, null, null);
        XDDFPieChartData.Series series = (XDDFPieChartData.Series)xddfPieChartData.addSeries(xAxisSource, yAxisSource);
        series.setTitle(null,null);
        // 为了在饼图上显示百分比等信息,需要调用下面的方法
        series.setShowLeaderLines(true);
        if (StrUtil.isEmpty(singleSeriesRenderData.getChartTitle())) {
            // 隐藏图例标识、系列名称、分类名称和数值
            CTPieSer ctPieSer = series.getCTPieSer();
            showCateName(ctPieSer, false);
            showVal(ctPieSer, false);
            showLegendKey(ctPieSer, false);
            showSerName(ctPieSer, false);
        }
        return xddfPieChartData;
    }

    public void showCateName(CTPieSer series, boolean val) {
        if (series.getDLbls().isSetShowCatName()) {
            series.getDLbls().getShowCatName().setVal(val);
        } else {
            series.getDLbls().addNewShowCatName().setVal(val);
        }
    }

    public void showVal(CTPieSer series, boolean val) {
        if (series.getDLbls().isSetShowVal()) {
            series.getDLbls().getShowVal().setVal(val);
        } else {
            series.getDLbls().addNewShowVal().setVal(val);
        }
    }

    public void showSerName(CTPieSer series, boolean val) {
        if (series.getDLbls().isSetShowSerName()) {
            series.getDLbls().getShowSerName().setVal(val);
        } else {
            series.getDLbls().addNewShowSerName().setVal(val);
        }
    }

    public void showLegendKey(CTPieSer series, boolean val) {
        if (series.getDLbls().isSetShowLegendKey()) {
            series.getDLbls().getShowLegendKey().setVal(val);
        } else {
            series.getDLbls().addNewShowLegendKey().setVal(val);
        }
    }


    private XDDFChartData performBarRendering(XWPFChart chart, ChartTypes chartType, CustomChartSingleSeriesRenderData singleSeriesRenderData
            , String[] xAxisData, Number[] yAxisData) {

        // 定义类别轴和数值轴
        XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
        XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
        leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);


        //设置X轴数据
        XDDFCategoryDataSource catSource = XDDFDataSourcesFactory.fromArray(xAxisData);
        //设置Y轴数据
        XDDFNumericalDataSource<Number> valSource = XDDFDataSourcesFactory.fromArray(yAxisData);

        // 创建柱状图数据系列
        XDDFBarChartData barChartData = (XDDFBarChartData) chart.createData(chartType, bottomAxis, leftAxis);
        XDDFBarChartData.Series series1 = (XDDFBarChartData.Series) barChartData.addSeries(catSource, valSource);
        series1.setTitle("示例系列", null); // 设置系列标题

        // 设置柱状图样式
        barChartData.setBarDirection(BarDirection.COL);
        return barChartData;
    }

    private XDDFChartData performRadarRendering(XWPFChart chart, ChartTypes chartType, CustomChartSingleSeriesRenderData singleSeriesRenderData
            , String[] xAxisData, Number[] yAxisData) {
        List<Number[]> list = new ArrayList<>();
        list.add(yAxisData);
        setRadarData(chart, new String[]{"系列一"}, xAxisData, list);
        return null;
    }

    private void setRadarData(XWPFChart chart, String[] series, String[] categories,
                              List<Number[]> list) {
        XDDFChartAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
        XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
        leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
        XDDFRadarChartData radar = (XDDFRadarChartData) chart
                .createData(org.apache.poi.xddf.usermodel.chart.ChartTypes.RADAR, bottomAxis, leftAxis);
        final int numOfPoints = categories.length;
        final String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0));
        final XDDFDataSource<?> categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange, 0);

        for (int i = 0; i < list.size(); i++) {
            final String valuesDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, i + 1, i + 1));
            final XDDFNumericalDataSource<? extends Number> valuesData = XDDFDataSourcesFactory.fromArray(list.get(i),
                    valuesDataRange, i);
            XDDFChartData.Series s = radar.addSeries(categoriesData, valuesData);
            s.setTitle(series[i], chart.setSheetTitle(series[i], i));
        }
        radar.setStyle(RadarStyle.STANDARD);
        chart.plot(radar);
        if (list.size() > 1) {
            XDDFChartLegend legend = chart.getOrAddLegend();
            legend.setPosition(LegendPosition.BOTTOM);
            legend.setOverlay(false);
        }
    }


    /**
     * LINE 渲染
     */
    private XDDFChartData performLineRendering(XWPFChart chart, ChartTypes chartType, CustomChartSingleSeriesRenderData singleSeriesRenderData
            , String[] xAxisData, Number[] yAxisData) {

        //图例设置
        XDDFChartLegend legend = chart.getOrAddLegend();
        //图例位置:上下左右
        legend.setPosition(LegendPosition.TOP);

        //X轴(分类轴)相关设置
        //创建X轴,并且指定位置
        XDDFCategoryAxis xAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);

        String xAxisTitle = singleSeriesRenderData.getxAxisTitle();

        //x轴标题
        if (StrUtil.isNotEmpty(xAxisTitle)) {
            xAxis.setTitle(xAxisTitle);
        }
        //Y轴(值轴)相关设置
        XDDFValueAxis yAxis = chart.createValueAxis(AxisPosition.LEFT); // 创建Y轴,指定位置
        if (StrUtil.isNotEmpty(singleSeriesRenderData.getyAxisTitle())) {
            yAxis.setTitle(singleSeriesRenderData.getyAxisTitle()); // Y轴标题
        }

        //创建折线图对象
        XDDFLineChartData customChartData = (XDDFLineChartData) chart.createData(chartType, xAxis, yAxis);
        //设置X轴数据
        XDDFCategoryDataSource xAxisSource = XDDFDataSourcesFactory.fromArray(xAxisData);
        //设置Y轴数据
        XDDFNumericalDataSource<Number> yAxisSource = XDDFDataSourcesFactory.fromArray(yAxisData);
        //加载折线图数据集
        XDDFLineChartData.Series lineSeries = (XDDFLineChartData.Series) customChartData.addSeries(xAxisSource, yAxisSource);
        //线条样式:true平滑曲线,false折线
        lineSeries.setSmooth(false);
        // 标记点样式
        lineSeries.setMarkerStyle(MarkerStyle.CIRCLE);
        //lineSeries.setMarkerSize((short) 5);

        return customChartData;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/930630.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MongoDB分片集群搭建及扩容

分片集群搭建及扩容 整体架构 环境准备 3台Linux虚拟机&#xff0c;准备MongoDB环境&#xff0c;配置环境变量。一定要版本一致&#xff08;重点&#xff09;&#xff0c;当前使用 version4.4.9 配置域名解析 在3台虚拟机上执行以下命令&#xff0c;注意替换实际 IP 地址 e…

docker desktop打包配置国内镜像地址

打包遇到无法访问外网资源&#xff0c;直接配置国内镜像地址 直接加入如下代码就行&#xff1a; {"builder": {"gc": {"defaultKeepStorage": "20GB","enabled": true}},"experimental": false,"registry-m…

嵌入式Linux,标准I/O探究,I/O缓冲,以及函数讲解

出于速度和效率的考虑&#xff0c;系统 I/O 调用&#xff08;即文件 I/O &#xff0c; open 、 read 、 write 等&#xff09;和标准 C 语言库 I/O 函数&#xff08;即标准 I/O 函数&#xff09;在操作磁盘文件时会对数据进行缓冲。 1. 文件 I/O 的内核缓冲 read() 和…

【人工智能】大数据平台技术及应用

文章目录 前言一、大数据平台基本概念及发展趋势1、数据量爆发式增长&#xff0c;发数据蓬勃发展2、大数据到底是什么&#xff1f;3、大数据处理与传统数据处理的差异4、为什么要建立大数据平台&#xff1f;5、大数据平台开源架构-Hadoop6、华为云大数据平台架构 二、大数据技术…

Word中的公式域

在WORD操作中&#xff0c;遇到数学公式时&#xff0c;我们往往都要通过公式编辑器来录入&#xff0c;其实&#xff0c;除了公式编辑器以外&#xff0c;在Word中还有一个编辑公式的利器&#xff1a;域。有了这个工具&#xff0c;应付一般的数学公式编辑还是绰绰有余的。 公式域的…

2.STM32通信接口之SPI通信---SPI实战《精讲》

SPI仅支持一主多从&#xff08;无应答机制&#xff09; 参照&#xff1a;《第十一部分》1.STM32通信接口之SPI通信---SPI介绍《精讲》-CSDN博客 在采用一主多从的模式下。从机未被选中&#xff0c;SN1时&#xff0c;从机的MISO会处于高阻态状态&#xff0c;SN0时&#xff0c;M…

电子电气架构 --- E/E(电子电气架构)的重新定义

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 所谓鸡汤&#xff0c;要么蛊惑你认命&#xff0c;要么怂恿你拼命&#xff0c;但都是回避问题的根源&…

小身躯大能量-供热系统通过EtherCAT转Profinet网关进行升级

在现代工业自动化领域&#xff0c;通信技术的进步对于提高系统效率、稳定性和可靠性起着至关重要的作用。EtherCAT&#xff08;Ethernet for Control Automation Technology&#xff09;作为一种实时以太网解决方案&#xff0c;因其高性能及成本效益高等特点&#xff0c;在众多…

buuctf:镜子里面的世界

查看图片属性以及010没有发现任何有用的信息 图片名字是steg.png,用stegsolve试试 flag{st3g0_saurus_wr3cks}

brpc的接口使用和封装

brpc 是用 c语言编写的工业级 RPC 框架&#xff0c;常用于搜索、存储、机器学习、广告、推荐等高性能系统。 brpc的远程调用思想&#xff1b;将数据处理的过程不在放在本地进行&#xff0c;而是放在服务器中去 接口使用 客户端和服务端的使用 服务端&#xff1a; 1.继承Echo…

.NET MAUI与.NET for Android/IOS的关系

2024年11月13日微软发布了.Net9.0,我打算体验一下。安装好.Net9.0 SDK后发现Visual Studio识别不到9.0&#xff0c;但是通过命令行dotnet --info查看是正常的&#xff0c;后面看到了VS有版本可以升级&#xff0c;把VS升级到17.12.0就可以了。更新完打开以后看到如下界面 这里…

聚焦 Facebook 隐私安全,守护用户数字家园

随着数字技术的飞速发展&#xff0c;社交媒体已成为我们生活中不可或缺的一部分&#xff0c;而隐私与安全的问题也愈加突出&#xff0c;特别是在 Facebook 这样拥有全球数十亿用户的平台上。如何有效地保障用户隐私&#xff0c;守护用户的数字家园&#xff0c;已成为社会各界关…

Linux C/C++编程的线程创建

【图书推荐】《Linux C与C一线开发实践&#xff08;第2版&#xff09;》_linux c与c一线开发实践pdf-CSDN博客《Linux C与C一线开发实践&#xff08;第2版&#xff09;&#xff08;Linux技术丛书&#xff09;》(朱文伟&#xff0c;李建英)【摘要 书评 试读】- 京东图书 (jd.com…

Gitee上获取renren-fast-vue install并run dev错误处理

目的&#xff1a;获取一个手脚架、越简约越好、越干净越好、于是看上了renren-fast-vue… 前端&#xff1a;vue2 后端&#xff1a;jdk1.8 mysql 5.7 SpringBoot单体架构 一开始只是下载前后端项目到本地&#xff0c;一堆乱七八糟的错误&#xff0c;网上找的资料也参差不齐… …

线程和进程(juc)

线程 一&#xff1a;概念辨析 1&#xff1a;线程与进程 进程&#xff1a; 1&#xff1a;程序由指令和数据组成&#xff0c;指令要执行&#xff0c;数据要读写&#xff0c;就需要将指令加载给cpu&#xff0c;把数据加载到内存&#xff0c;同时程序运行时还会使用磁盘&#x…

五、docker的网络模式

五、docker的网络模式 5.1 Docker的四种网络模式 当你安装docker时&#xff0c;它会自动创建三个网络&#xff0c;可使用如下命令查看&#xff1a; [rootlocalhost ~]# docker network ls NETWORK ID NAME DRIVER SCOPE 7390284b02d6 bridge bridge lo…

【AWS re:Invent 2024】一文了解EKS新功能:Amazon EKS Auto Mode

文章目录 一、为什么要使用 Amazon EKS Auto Mode&#xff1f;二、Amazon EKS自动模式特性2.1 持续优化计算成本2.2 迁移集群操作2.3 EKS 自动模式的高级功能 三、EKS Auto 集群快速创建集群配置四、查看来自 API 服务器的指标五、EKS 相关角色权限设置六、参考链接 一、为什么…

数据结构——有序二叉树的删除

在上一篇博客中&#xff0c;我们介绍了有序二叉树的构建、遍历、查找。 数据结构——有序二叉树的构建&遍历&查找-CSDN博客文章浏览阅读707次&#xff0c;点赞18次&#xff0c;收藏6次。因为数据的类型决定数据在内存中的存储形式。left right示意为左右节点其类型也为…

git pull error: cannot lock ref

Git: cannot lock ref ‘refs/remotes/origin/feature/xxx’: refs/remotes/origin/feature/xxx/car’ exists; cannot create refs/remotes/origin/feature/xxx git remote prune origin重新整理服务端和本地的关联关系即可

树与图深度优先遍历——acwing

题目一&#xff1a;树的重心 846. 树的重心 - AcWing题库 分析 采用暴力枚举&#xff0c;试探每个点&#xff0c;除去之后&#xff0c;连通分量最大值是多少&#xff0c; 各个点的最大值找最小的 因为可以通过 dfs 来得到 根u以下点数&#xff0c;以及可以求各分树的点数&am…