Bläddra i källkod

试验-保存
1、添加图表公式(初步)

LHB 1 månad sedan
förälder
incheckning
e63df89627

+ 79 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/FormulaServiceImpl.java

@@ -5058,8 +5058,87 @@ public class FormulaServiceImpl extends BaseServiceImpl<FormulaMapper, Formula>
      */
     public void preCalc(FormData fd, TableElementConverter tec) {
         try {
+            if(fd.getCode().equals("m_20230410104313_1645256088125177856:key_19")){
+                System.out.println();
+            }
             Formula formula = fd.getFormula();
             String f = formula.getFormula();
+            //图表公式
+            if(f.contains("chart")){
+                Matcher m = RegexUtils.matcher(FC_REG + "(chart)\\(([^)]+)\\)", f);
+                while (m.find()) {
+                    String[] args = m.group(2).split(",");
+                    //y轴源数据
+                    List<FormData> target = getFormDataByCode(args[0], tec);
+                    //x轴源数据
+                    List<FormData> target1 = getFormDataByCode(args[1], tec);
+
+                    List<Double> x = new ArrayList<>();
+                    List<Double> y = new ArrayList<>();
+
+                    if (!target.isEmpty()) {
+                        FormData a = target.get(0);
+                        for (ElementData value : a.getValues()) {
+                            if(ConvertUtils.canConvertToDouble(value.getValue())){
+                                y.add(Double.valueOf(value.getValue().toString()));
+                            }
+                        }
+
+                    }
+                    //含水率是x轴
+                    if (!target1.isEmpty()) {
+                        FormData a = target1.get(0);
+                        for (ElementData value : a.getValues()) {
+                            if(ConvertUtils.canConvertToDouble(value.getValue())){
+                                x.add(Double.valueOf(value.getValue().toString()));
+                            }
+                        }
+                    }
+                    String url = "";
+                    if(CollectionUtil.isNotEmpty(x) && CollectionUtil.isNotEmpty(y)){
+                        //数据错误
+                        if(x.size() != y.size()){
+                            break;
+                        }
+                        double[][] points = new double[x.size()][2];
+                        for (int i = 0; i < x.size(); i++) {
+                            points[i][0] = x.get(i);
+                            points[i][1] = y.get(i);
+                        }
+
+                        Long id = SnowFlakeUtil.getId();
+
+                        //图片存放路径
+//                        String filePath = ParamCache.getValue(CommonConstant.SYS_LOCAL_URL);
+//                        String listPdf = filePath + "/pdf/" + id + ".png";
+                        String listPdf = "C:\\upload\\pdf\\" + id + ".png";
+
+                        //土界含水率试验检测记录表  TODO 存在两张类似的表但是init_table_name 不同
+                        if(fd.getCode().contains("m_20230410104313_1645256088125177856")){
+                            // 创建图表生成器
+                            SoilTestChart generator = new SoilTestChart(points);
+                            // 生成图表并保存为图片(不显示GUI窗口)
+                            generator.generateChart(listPdf, 400, 550);
+                        }else if (false){
+
+                        }
+
+                        File tabPDF = ResourceUtil.getFile(listPdf);
+                        //上传至oss
+                        BladeFile bladeFile = this.newIOSSClient.uploadFile(id + ".png", listPdf);
+                        if (tabPDF.exists()) {
+                            tabPDF.delete();
+                        }
+                        if (bladeFile != null) {
+                            url = bladeFile.getLink();
+                        }
+                    }
+
+                    //生成图片返回
+                    f = f.replace(m.group(), putDataWithKey(url, tec));
+                }
+            }
+
             if (f.contains("converge")) {
                 Matcher m = RegexUtils.matcher(FC_REG + "(converge)\\(([^)]+)\\)", f);
                 while (m.find()) {

+ 36 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/utils/ConvertUtils.java

@@ -0,0 +1,36 @@
+package org.springblade.manager.utils;
+
+import java.util.Optional;
+
+/**
+ * 类型转换
+ * @author LHB
+ */
+public class ConvertUtils {
+
+    public static boolean canConvertToDouble(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        // 如果是数字类型,除了Double,还有Integer、Long等都可以转成double
+        if (obj instanceof Number) {
+            return true;
+        }
+        // 如果是字符串
+        if (obj instanceof String) {
+            String str = (String) obj;
+            // 去除千分位中的逗号
+            String withoutCommas = str.replace(",", "");
+            try {
+                // 尝试转换
+                Double.parseDouble(withoutCommas);
+                return true;
+            } catch (NumberFormatException e) {
+                // 转换失败
+                return false;
+            }
+        }
+        // 其他类型,无法转换
+        return false;
+    }
+}

+ 314 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/utils/SoilTestChart.java

@@ -0,0 +1,314 @@
+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.XYTextAnnotation;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.axis.NumberTickUnit;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.chart.ui.RectangleInsets;
+import org.jfree.data.xy.XYDataset;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+
+import java.awt.*;
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * 界限含水率
+ * @author LHB
+ */
+public class SoilTestChart {
+
+    // 定义关键数据点
+    private double[][] dataPoints; // 存储原始数据点
+    private double w_avg; // C'点含水率
+    private double wl;    // WL含水率
+
+    public SoilTestChart(double[][] points) {
+        // 确保数据点按含水率排序(a点含水率最大,在最右边)
+        this.dataPoints = sortDataPoints(points);
+    }
+
+    // 按含水率排序数据点(确保a点在最右边)
+    private double[][] sortDataPoints(double[][] points) {
+        // 复制数组以避免修改原始数据
+        double[][] sorted = new double[points.length][2];
+        for (int i = 0; i < points.length; i++) {
+            sorted[i][0] = points[i][0]; // 含水率
+            sorted[i][1] = points[i][1]; // 锥入深度
+        }
+
+        // 按含水率升序排序
+        Arrays.sort(sorted, Comparator.comparingDouble(a -> a[0]));
+        return sorted;
+    }
+
+    private XYDataset createDataset() {
+        // 提取排序后的点
+        double moistureC = dataPoints[0][0]; // C点含水率(最小)
+        double depthC = dataPoints[0][1];    // C点锥入深度
+
+        double moistureB = dataPoints[1][0]; // B点含水率
+        double depthB = dataPoints[1][1];    // B点锥入深度
+
+        double moistureA = dataPoints[2][0]; // A点含水率(最大)
+        double depthA = dataPoints[2][1];    // A点锥入深度
+
+        // 计算AB和AC线上h=2mm时的含水率
+        double w_ab = calculateWaterContent(moistureA, depthA, moistureB, depthB, 2.0);
+        double w_ac = calculateWaterContent(moistureA, depthA, moistureC, depthC, 2.0);
+
+        // 计算平均含水率作为C'点
+        w_avg = (w_ab + w_ac) / 2.0;
+
+        // 创建AC'线数据集 - 只包含A点和C'点
+        XYSeries seriesACPrime = new XYSeries("AC'线");
+        seriesACPrime.add(moistureA, depthA); // A点
+        seriesACPrime.add(w_avg, 2.0);        // C'点
+
+        // 计算h=20mm时的含水率(WL)
+        wl = calculateWaterContent(moistureA, depthA, w_avg, 2.0, 20.0);
+
+        // 创建WL和WP的虚线数据集
+        XYSeries seriesWL = new XYSeries("WL");
+        seriesWL.add(wl, 0);
+        seriesWL.add(wl, 20);
+
+        XYSeries seriesWP = new XYSeries("WP");
+        seriesWP.add(w_avg, 0);
+        seriesWP.add(w_avg, 2);
+
+        XYSeries seriesWLHorizontal = new XYSeries("WL_H");
+        seriesWLHorizontal.add(0, 20);
+        seriesWLHorizontal.add(wl, 20);
+
+        XYSeries seriesWPHorizontal = new XYSeries("WP_H");
+        seriesWPHorizontal.add(0, 2);
+        seriesWPHorizontal.add(w_avg, 2);
+
+        // 创建原始点数据集
+        XYSeries seriesPoints = new XYSeries("原始点");
+        seriesPoints.add(moistureA, depthA); // A点
+        seriesPoints.add(moistureB, depthB); // B点
+        seriesPoints.add(moistureC, depthC); // C点
+
+        // 创建C'点数据集(单独显示)
+        XYSeries seriesCPrime = new XYSeries("C'点");
+        seriesCPrime.add(w_avg, 2.0);
+
+        // 将所有系列添加到数据集
+        XYSeriesCollection dataset = new XYSeriesCollection();
+        dataset.addSeries(seriesACPrime);
+        dataset.addSeries(seriesWL);
+        dataset.addSeries(seriesWP);
+        dataset.addSeries(seriesWLHorizontal);
+        dataset.addSeries(seriesWPHorizontal);
+        dataset.addSeries(seriesPoints);
+        dataset.addSeries(seriesCPrime);
+
+        return dataset;
+    }
+
+    // 计算直线上给定深度对应的含水率
+    private double calculateWaterContent(double x1, double y1, double x2, double y2, double targetY) {
+        double k = (y2 - y1) / (x2 - x1);
+        double b = y1 - k * x1;
+        return (targetY - b) / k;
+    }
+
+    private JFreeChart createChart(XYDataset dataset) {
+        // 提取排序后的点
+        double moistureC = dataPoints[0][0]; // C点含水率(最小)
+        double depthC = dataPoints[0][1];    // C点锥入深度
+
+        double moistureB = dataPoints[1][0]; // B点含水率
+        double depthB = dataPoints[1][1];    // B点锥入深度
+
+        double moistureA = dataPoints[2][0]; // A点含水率(最大)
+        double depthA = dataPoints[2][1];    // A点锥入深度
+
+        // 创建折线图
+        JFreeChart chart = ChartFactory.createXYLineChart(
+                "锥入深度与含水率(h-w)关系",      // 图表标题
+                "含水率w(%)",           // x轴标签
+                "锥入深度h(mm)",       // y轴标签
+                dataset,              // 数据集
+                PlotOrientation.VERTICAL,
+                false,               // 不显示图例
+                false,               // 不显示提示
+                false                // 不显示URL
+        );
+
+        // 调整图表边距
+        chart.setPadding(new RectangleInsets(10, 10, 10, 10));
+
+        // 解决中文乱码问题
+        chart.getTitle().setFont(new Font("宋体", Font.BOLD, 18));
+
+        // 获取图表区域对象
+        XYPlot plot = chart.getXYPlot();
+
+        // 设置x轴
+        NumberAxis xAxis = (NumberAxis) plot.getDomainAxis();
+        xAxis.setLabelFont(new Font("宋体", Font.BOLD, 14));
+        xAxis.setTickLabelFont(new Font("宋体", Font.BOLD, 12));
+        xAxis.setTickMarkOutsideLength(-5.0f);
+        xAxis.setTickMarksVisible(true);
+        xAxis.setAutoRangeIncludesZero(false);
+        // 设置x轴刻度间隔为10
+        xAxis.setTickUnit(new NumberTickUnit(10));
+        // 设置x轴范围,根据数据调整
+        xAxis.setRange(0, 100);
+
+        // 设置y轴
+        NumberAxis yAxis = (NumberAxis) plot.getRangeAxis();
+        yAxis.setLabelFont(new Font("宋体", Font.BOLD, 14));
+        yAxis.setTickLabelFont(new Font("宋体", Font.PLAIN, 12));
+        yAxis.setTickMarkOutsideLength(-5.0f);
+        yAxis.setTickMarksVisible(true);
+
+        //设置坐标轴偏移
+        plot.setAxisOffset(new RectangleInsets(0,0,0,0));
+        // 设置渲染器
+        XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
+
+        // AC'线 - 实线(只连接A点和C'点)
+        renderer.setSeriesPaint(0, Color.BLACK);
+        renderer.setSeriesStroke(0, new BasicStroke(2.0f));
+        renderer.setSeriesShapesVisible(0, false); // 不显示形状
+
+        // WL虚线 - 红色
+        renderer.setSeriesPaint(1, Color.BLACK);
+        renderer.setSeriesStroke(1, new BasicStroke(
+                1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
+                10.0f, new float[]{5.0f, 5.0f}, 0.0f
+        ));
+        renderer.setSeriesShapesVisible(1, false);
+
+        // WP虚线 - 绿色
+        renderer.setSeriesPaint(2, Color.BLACK);
+        renderer.setSeriesStroke(2, new BasicStroke(
+                1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
+                10.0f, new float[]{5.0f, 5.0f}, 0.0f
+        ));
+        renderer.setSeriesShapesVisible(2, false);
+
+        // WL水平虚线 - 红色
+        renderer.setSeriesPaint(3, Color.BLACK);
+        renderer.setSeriesStroke(3, new BasicStroke(
+                1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
+                10.0f, new float[]{5.0f, 5.0f}, 0.0f
+        ));
+        renderer.setSeriesShapesVisible(3, false);
+
+        // WP水平虚线 - 绿色
+        renderer.setSeriesPaint(4, Color.BLACK);
+        renderer.setSeriesStroke(4, new BasicStroke(
+                1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
+                10.0f, new float[]{5.0f, 5.0f}, 0.0f
+        ));
+        renderer.setSeriesShapesVisible(4, false);
+
+        // 原始点 - 不连线,只显示形状
+        renderer.setSeriesPaint(5, Color.BLACK);
+        renderer.setSeriesLinesVisible(5, false);
+        renderer.setSeriesShapesVisible(5, true);
+        renderer.setSeriesShape(5, new java.awt.geom.Ellipse2D.Double(-4, -4, 8, 8));
+
+        // C'点 - 不连线,只显示形状
+        renderer.setSeriesPaint(6, Color.BLACK);
+        renderer.setSeriesLinesVisible(6, false);
+        renderer.setSeriesShapesVisible(6, true);
+        renderer.setSeriesShape(6, new java.awt.geom.Ellipse2D.Double(-4, -4, 8, 8));
+
+        plot.setRenderer(renderer);
+
+        // 设置背景色
+        plot.setBackgroundPaint(Color.WHITE);
+        // 移除水平方向网格线
+        plot.setDomainGridlinesVisible(false);
+        // 移除垂直方向网格线
+        plot.setRangeGridlinesVisible(false);
+        // 禁用绘图区域外边框
+//        plot.setOutlineVisible(false);
+
+        // A点标签(含水率最大,在最右边)
+        XYTextAnnotation aLabel = new XYTextAnnotation("A", moistureA - 1, depthA + 0.5);
+        aLabel.setFont(new Font("宋体", Font.BOLD, 14));
+        aLabel.setPaint(Color.BLACK);
+        plot.addAnnotation(aLabel);
+
+        // B点标签
+        XYTextAnnotation bLabel = new XYTextAnnotation("B", moistureB - 1, depthB + 0.5);
+        bLabel.setFont(new Font("宋体", Font.BOLD, 14));
+        bLabel.setPaint(Color.BLACK);
+        plot.addAnnotation(bLabel);
+
+        // C点标签(含水率最小,在最左边)
+        XYTextAnnotation cLabel = new XYTextAnnotation("C", moistureC - 1, depthC + 0.5);
+        cLabel.setFont(new Font("宋体", Font.BOLD, 14));
+        cLabel.setPaint(Color.BLACK);
+        plot.addAnnotation(cLabel);
+
+        // C'点标签
+        XYTextAnnotation cPrimeLabel = new XYTextAnnotation("C'", w_avg - 1, 2.0 + 0.5);
+        cPrimeLabel.setFont(new Font("宋体", Font.BOLD, 14));
+        cPrimeLabel.setPaint(Color.BLACK);
+        plot.addAnnotation(cPrimeLabel);
+
+        // WL标签(x轴上方)
+        XYTextAnnotation wlLabel = new XYTextAnnotation("WL", wl - 1, 0.5);
+        wlLabel.setFont(new Font("宋体", Font.BOLD, 12));
+        wlLabel.setPaint(Color.BLACK);
+        plot.addAnnotation(wlLabel);
+
+        // WP标签(x轴上方)
+        XYTextAnnotation wpLabel = new XYTextAnnotation("WP", w_avg - 1, 0.5);
+        wpLabel.setFont(new Font("宋体", Font.BOLD, 12));
+        wpLabel.setPaint(Color.BLACK);
+        plot.addAnnotation(wpLabel);
+
+        return chart;
+    }
+
+    // 生成图表并保存为图片
+    public void generateChart(String filename, int width, int height) {
+        try {
+            // 创建数据集
+            XYDataset dataset = createDataset();
+
+            // 创建图表
+            JFreeChart chart = createChart(dataset);
+
+            // 保存图表为图片
+            ChartUtils.saveChartAsPNG(new File(filename), chart, width, height);
+            System.out.println("图表已保存为 " + filename);
+        } catch (IOException e) {
+            System.err.println("保存图表时出错: " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+
+    public static void main11(String[] args) {
+        // 示例数据:二维数组,每行表示一个点 [含水率, 锥入深度]
+        // 注意:a点应该含水率最大,在最右边
+        double[][] points = {
+                {25.0, 5.0},  // C点(含水率最小)
+                {35.0, 15.0}, // B点
+                {45.0, 25.0}  // A点(含水率最大)
+        };
+
+        // 创建图表生成器
+        SoilTestChart generator = new SoilTestChart(points);
+
+        // 生成图表并保存为图片(不显示GUI窗口)
+        generator.generateChart("SoilTestChart.png", 500, 600);
+    }
+}