如何使用Apache POI获取图表自动设置的majorUnit等坐标轴属性值
Apache POI 获取坐标轴自动刻度属性解决方案
当坐标轴刻度设置为自动模式时,XDDFValueAxis.getMajorUnit()这类封装方法只会返回手动设置过的固定值,Office自动计算的实际值不会直接在XDDF层暴露,可通过以下两种方案获取:
方案1:从底层OOXML对象读取已生成的自动刻度值
如果图表已经被Office打开并保存过,自动计算的刻度值会存储在底层CTValAx结构中,可直接读取:
import org.apache.poi.xddf.usermodel.chart.XDDFValueAxis; import org.openxmlformats.schemas.drawingml.x2006.chart.CTValAx; import org.openxmlformats.schemas.drawingml.x2006.chart.CTScaling; // 替换为你已获取的XDDFValueAxis实例 XDDFValueAxis axis = ...; // 读取底层结构 CTValAx ctValAx = axis.getCTValAx(); CTScaling scaling = ctValAx.getScaling(); // 读取各属性,不存在则返回null(说明未经过Office自动计算保存) Double majorUnit = ctValAx.isSetMajorUnit() ? ctValAx.getMajorUnit() : null; Double minorUnit = ctValAx.isSetMinorUnit() ? ctValAx.getMinorUnit() : null; Double upperBound = scaling.isSetMax() ? scaling.getMax().getVal() : null; Double lowerBound = scaling.isSetMin() ? scaling.getMin().getVal() : null;
注意:刚通过POI生成、未经过Office打开保存的图表无法用该方法拿到值,因为POI不会自动计算刻度。
方案2:根据系列数据手动计算自动刻度值
如果是刚生成未经过Office处理的图表,可按照Office默认的刻度规则手动计算:
- 先统计所有绑定到当前值轴的系列数据的最大值、最小值
- 按照期望的主刻度数量(默认5-10个)计算初始单位,再规整为1/2/5/10这类规整量级的数值
- 次刻度单位一般取主刻度的1/2或1/5
- 坐标轴上下限取主刻度单位的整数倍
示例计算代码:
import org.apache.poi.xddf.usermodel.chart.XDDFChartData; import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource; // 从图表的XDDFChartData中获取绑定到当前值轴的所有系列 List<XDDFChartData.Series> seriesList = ...; double minVal = Double.MAX_VALUE; double maxVal = Double.MIN_VALUE; // 统计所有数据点的极值 for (XDDFChartData.Series series : seriesList) { XDDFNumericalDataSource<?> values = series.getValuesData(); for (int i = 0; i < values.getPointCount(); i++) { double val = ((Number) values.getPointAt(i)).doubleValue(); minVal = Math.min(minVal, val); maxVal = Math.max(maxVal, val); } } // 计算规整刻度 double range = maxVal == minVal ? 1 : maxVal - minVal; int desiredMajorTicks = 6; // 期望的主刻度数量,可调整 double roughUnit = range / desiredMajorTicks; double exponent = Math.floor(Math.log10(roughUnit)); double fraction = roughUnit / Math.pow(10, exponent); double niceFraction = 1; if (fraction >= 1.5 && fraction < 3) niceFraction = 2; else if (fraction >=3 && fraction <7) niceFraction =5; else if (fraction >=7) niceFraction =10; double majorUnit = niceFraction * Math.pow(10, exponent); double minorUnit = majorUnit /5; // 可调整为majorUnit/2 double lowerBound = Math.floor(minVal / majorUnit) * majorUnit; double upperBound = Math.ceil(maxVal / majorUnit) * majorUnit;
内容的提问来源于stack exchange,提问作者Kannan Sundararaj
相关产品推荐
相关产品推荐

