|
@@ -0,0 +1,272 @@
|
|
|
+package org.springblade.manager.utils;
|
|
|
+import org.jfree.chart.ChartFactory;
|
|
|
+import org.jfree.chart.ChartUtils;
|
|
|
+import org.jfree.chart.JFreeChart;
|
|
|
+import org.jfree.chart.annotations.XYLineAnnotation;
|
|
|
+import org.jfree.chart.annotations.XYTextAnnotation;
|
|
|
+import org.jfree.chart.axis.NumberAxis;
|
|
|
+import org.jfree.chart.plot.PlotOrientation;
|
|
|
+import org.jfree.chart.plot.XYPlot;
|
|
|
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
|
|
|
+import org.jfree.chart.ui.RectangleEdge;
|
|
|
+import org.jfree.chart.ui.RectangleInsets;
|
|
|
+import org.jfree.chart.ui.TextAnchor;
|
|
|
+import org.jfree.data.category.DefaultCategoryDataset;
|
|
|
+import org.jfree.data.xy.XYSeries;
|
|
|
+import org.jfree.data.xy.XYSeriesCollection;
|
|
|
+
|
|
|
+import java.awt.*;
|
|
|
+import java.io.File;
|
|
|
+import java.io.IOException;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 凝结时间差
|
|
|
+ * @author LHB
|
|
|
+ */
|
|
|
+public class PenetrationResistanceChart {
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成贯入阻力图表并保存为图片
|
|
|
+ * @param data 二维数组,第一列为时间(分钟),第二列为贯入阻力(MPa)
|
|
|
+ * @param outputPath 输出图片路径
|
|
|
+ * @return 成功返回true,失败返回false
|
|
|
+ */
|
|
|
+ public static boolean generateChart(double[][] data, String outputPath, int width, int height) {
|
|
|
+ try {
|
|
|
+ // 创建数据集 - 主曲线
|
|
|
+ XYSeries mainSeries = new XYSeries("贯入阻力");
|
|
|
+ for (double[] point : data) {
|
|
|
+ mainSeries.add(point[0], point[1]);
|
|
|
+ }
|
|
|
+
|
|
|
+ XYSeriesCollection dataset = new XYSeriesCollection();
|
|
|
+ dataset.addSeries(mainSeries);
|
|
|
+
|
|
|
+ // 创建支持中文的字体
|
|
|
+ Font chineseFont = new Font("SimSun", Font.BOLD, 12);
|
|
|
+ Font titleFont = new Font("SimHei", Font.BOLD, 16);
|
|
|
+ Font legendFont = new Font("SimSun", Font.BOLD, 10);
|
|
|
+
|
|
|
+ // 创建图表
|
|
|
+ JFreeChart chart = ChartFactory.createXYLineChart(
|
|
|
+ "减水剂凝结时间差试验曲线",
|
|
|
+ "时间 (分钟)",
|
|
|
+ "贯入阻力 (MPa)",
|
|
|
+ dataset,
|
|
|
+ PlotOrientation.VERTICAL,
|
|
|
+ true, // 包含图例
|
|
|
+ true,
|
|
|
+ false
|
|
|
+ );
|
|
|
+
|
|
|
+ // 设置中文字体
|
|
|
+ chart.getTitle().setFont(titleFont);
|
|
|
+ chart.getLegend().setItemFont(legendFont);
|
|
|
+
|
|
|
+ // 设置图表背景和边距
|
|
|
+ chart.setBackgroundPaint(Color.WHITE);
|
|
|
+ chart.setPadding(new RectangleInsets(15, 15, 15, 15));
|
|
|
+
|
|
|
+ // 获取图表区域对象
|
|
|
+ XYPlot plot = (XYPlot) chart.getPlot();
|
|
|
+ plot.setBackgroundPaint(Color.WHITE);
|
|
|
+ plot.setDomainGridlinePaint(Color.LIGHT_GRAY);
|
|
|
+ plot.setRangeGridlinePaint(Color.LIGHT_GRAY);
|
|
|
+ plot.setAxisOffset(new RectangleInsets(5, 5, 5, 5));
|
|
|
+
|
|
|
+ // 配置X轴(设置中文字体)
|
|
|
+ NumberAxis xAxis = (NumberAxis) plot.getDomainAxis();
|
|
|
+ xAxis.setAutoRangeIncludesZero(true);
|
|
|
+ xAxis.setLabelFont(chineseFont);
|
|
|
+ xAxis.setTickLabelFont(chineseFont);
|
|
|
+ xAxis.setTickMarkOutsideLength(5.0f);
|
|
|
+
|
|
|
+ // 配置Y轴(设置中文字体)
|
|
|
+ NumberAxis yAxis = (NumberAxis) plot.getRangeAxis();
|
|
|
+ yAxis.setAutoRangeIncludesZero(true);
|
|
|
+ yAxis.setLabelFont(chineseFont);
|
|
|
+ yAxis.setTickLabelFont(chineseFont);
|
|
|
+ yAxis.setTickMarkOutsideLength(5.0f);
|
|
|
+
|
|
|
+ plot.setAxisOffset(new RectangleInsets(0,0,0,0));
|
|
|
+
|
|
|
+ // 设置渲染器
|
|
|
+ XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
|
|
|
+
|
|
|
+ // 主曲线样式
|
|
|
+ renderer.setSeriesPaint(0, Color.BLUE);
|
|
|
+ renderer.setSeriesFillPaint(0, new Color(255,255,255,255));
|
|
|
+ //设置空心
|
|
|
+ renderer.setUseFillPaint(true);
|
|
|
+ renderer.setSeriesShape(0, new java.awt.geom.Ellipse2D.Double(-3, -3, 6, 6));
|
|
|
+ // 主曲线不显示点
|
|
|
+ renderer.setSeriesShapesVisible(0, true);
|
|
|
+ plot.setRenderer(0, renderer);
|
|
|
+
|
|
|
+ // 查找3.5MPa和28MPa对应的点
|
|
|
+ double target1 = 3.5;
|
|
|
+ double target2 = 28.0;
|
|
|
+ Double timeAt35 = null;
|
|
|
+ Double resistanceAt35 = target1;
|
|
|
+ Double timeAt28 = null;
|
|
|
+ Double resistanceAt28 = target2;
|
|
|
+
|
|
|
+ for (int i = 0; i < data.length - 1; i++) {
|
|
|
+ // 查找3.5MPa
|
|
|
+ if (timeAt35 == null &&
|
|
|
+ ((data[i][1] <= target1 && data[i+1][1] >= target1) ||
|
|
|
+ (data[i][1] >= target1 && data[i+1][1] <= target1))) {
|
|
|
+ // 线性插值计算时间
|
|
|
+ double x1 = data[i][0];
|
|
|
+ double y1 = data[i][1];
|
|
|
+ double x2 = data[i+1][0];
|
|
|
+ double y2 = data[i+1][1];
|
|
|
+ timeAt35 = x1 + (target1 - y1) * (x2 - x1) / (y2 - y1);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查找28MPa
|
|
|
+ if (timeAt28 == null &&
|
|
|
+ ((data[i][1] <= target2 && data[i+1][1] >= target2) ||
|
|
|
+ (data[i][1] >= target2 && data[i+1][1] <= target2))) {
|
|
|
+ // 线性插值计算时间
|
|
|
+ double x1 = data[i][0];
|
|
|
+ double y1 = data[i][1];
|
|
|
+ double x2 = data[i+1][0];
|
|
|
+ double y2 = data[i+1][1];
|
|
|
+ timeAt28 = x1 + (target2 - y1) * (x2 - x1) / (y2 - y1);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (timeAt35 != null && timeAt28 != null) break;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // 为A点和B点添加虚拟系列用于图例
|
|
|
+ XYSeriesCollection pointDataset = new XYSeriesCollection();
|
|
|
+ renderer = new XYLineAndShapeRenderer();
|
|
|
+ int index = 0;
|
|
|
+ // 添加特殊点到数据集
|
|
|
+ if (timeAt35 != null) {
|
|
|
+ // 创建特殊点数据集
|
|
|
+ XYSeries point35Series = new XYSeries(String.format("初凝点(3.5MPa, %.0f分钟)", timeAt35));
|
|
|
+ point35Series.add(timeAt35, resistanceAt35);
|
|
|
+ pointDataset.addSeries(point35Series);
|
|
|
+ // 3.5MPa点样式
|
|
|
+ renderer.setSeriesPaint(index, Color.RED);
|
|
|
+ renderer.setSeriesStroke(index, new BasicStroke(1.0f));
|
|
|
+ // 圆形标记
|
|
|
+ renderer.setSeriesShape(index, new java.awt.geom.Ellipse2D.Double(-5, -5, 10, 10));
|
|
|
+ renderer.setSeriesShapesVisible(index, true);
|
|
|
+ // 不连接线
|
|
|
+ renderer.setSeriesLinesVisible(index, false);
|
|
|
+ index++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (timeAt28 != null) {
|
|
|
+ XYSeries point28Series = new XYSeries(String.format("终凝点(28MPa,%.0f分钟)", timeAt28));
|
|
|
+ point28Series.add(timeAt28, resistanceAt28);
|
|
|
+ pointDataset.addSeries(point28Series);
|
|
|
+ // 28MPa点样式
|
|
|
+ renderer.setSeriesPaint(index, Color.GREEN);
|
|
|
+ renderer.setSeriesStroke(index, new BasicStroke(1.0f));
|
|
|
+ // 圆形标记
|
|
|
+ renderer.setSeriesShape(index, new java.awt.geom.Ellipse2D.Double(-5, -5, 10, 10));
|
|
|
+ renderer.setSeriesShapesVisible(index, true);
|
|
|
+ // 不连接线
|
|
|
+ renderer.setSeriesLinesVisible(index, false);
|
|
|
+
|
|
|
+ }
|
|
|
+ //自定义点位
|
|
|
+ plot.setDataset(1, pointDataset);
|
|
|
+ //自定义点位样式
|
|
|
+ plot.setRenderer(1,renderer);
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ // 添加3.5MPa的标注和虚线
|
|
|
+ if (timeAt35 != null) {
|
|
|
+
|
|
|
+
|
|
|
+ // 水平虚线
|
|
|
+ XYLineAnnotation hLine35 = new XYLineAnnotation(
|
|
|
+ plot.getDomainAxis().getLowerBound(), target1,
|
|
|
+ plot.getDomainAxis().getUpperBound(), target1,
|
|
|
+ new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
|
|
|
+ 10.0f, new float[]{5.0f, 5.0f}, 0.0f),
|
|
|
+ Color.RED
|
|
|
+ );
|
|
|
+ plot.addAnnotation(hLine35);
|
|
|
+
|
|
|
+ // 垂直虚线
|
|
|
+ XYLineAnnotation vLine35 = new XYLineAnnotation(
|
|
|
+ timeAt35, plot.getRangeAxis().getLowerBound(),
|
|
|
+ timeAt35, plot.getRangeAxis().getUpperBound(),
|
|
|
+ new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
|
|
|
+ 10.0f, new float[]{5.0f, 5.0f}, 0.0f),
|
|
|
+ Color.RED
|
|
|
+ );
|
|
|
+ plot.addAnnotation(vLine35);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加28MPa的标注和虚线
|
|
|
+ if (timeAt28 != null) {
|
|
|
+ // 水平虚线
|
|
|
+ XYLineAnnotation hLine28 = new XYLineAnnotation(
|
|
|
+ plot.getDomainAxis().getLowerBound(), target2,
|
|
|
+ plot.getDomainAxis().getUpperBound(), target2,
|
|
|
+ new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
|
|
|
+ 10.0f, new float[]{5.0f, 5.0f}, 0.0f),
|
|
|
+ Color.GREEN
|
|
|
+ );
|
|
|
+ plot.addAnnotation(hLine28);
|
|
|
+
|
|
|
+ // 垂直虚线
|
|
|
+ XYLineAnnotation vLine28 = new XYLineAnnotation(
|
|
|
+ timeAt28, plot.getRangeAxis().getLowerBound(),
|
|
|
+ timeAt28, plot.getRangeAxis().getUpperBound(),
|
|
|
+ new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
|
|
|
+ 10.0f, new float[]{5.0f, 5.0f}, 0.0f),
|
|
|
+ Color.GREEN
|
|
|
+ );
|
|
|
+ plot.addAnnotation(vLine28);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 调整图例位置到左上角
|
|
|
+ chart.getLegend().setBackgroundPaint(new Color(255, 255, 255, 200)); // 半透明背景
|
|
|
+ chart.getLegend().setPosition(RectangleEdge.TOP);
|
|
|
+
|
|
|
+
|
|
|
+ // 保存图表为PNG文件
|
|
|
+ File outputFile = new File(outputPath);
|
|
|
+ ChartUtils.saveChartAsPNG(outputFile, chart, width, height);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ } catch (IOException e) {
|
|
|
+ System.err.println("保存图表时发生错误: " + e.getMessage());
|
|
|
+ return false;
|
|
|
+ } catch (Exception e) {
|
|
|
+ System.err.println("生成图表时发生错误: " + e.getMessage());
|
|
|
+ e.printStackTrace();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void main11(String[] args) {
|
|
|
+ // 示例数据:时间(分钟)和贯入阻力(MPa)
|
|
|
+ double[][] data = {
|
|
|
+ {0, 0.5}, {10, 1.2}, {20, 2.1}, {30, 3.0}, {40, 3.8},
|
|
|
+ {50, 5.2}, {60, 7.5}, {70, 10.3}, {80, 14.2}, {90, 18.5},
|
|
|
+ {100, 22.8}, {110, 26.5}, {120, 29.2}, {130, 31.5}, {140, 33.2},
|
|
|
+ {150, 34.5}, {160, 35.2}, {170, 35.8}, {180, 36.0}
|
|
|
+ };
|
|
|
+
|
|
|
+ // 生成图表并保存为图片
|
|
|
+ boolean success = generateChart(data, "penetration_resistance_chart.png",420,210);
|
|
|
+
|
|
|
+ if (success) {
|
|
|
+ System.out.println("图表已成功生成并保存为 'penetration_resistance_chart.png'");
|
|
|
+ } else {
|
|
|
+ System.out.println("图表生成失败");
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|