package com.gunshi.project.hsz.service; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.gunshi.project.hsz.mapper.StZvarlBMapper; import com.gunshi.project.hsz.model.StZqrlB; import com.gunshi.project.hsz.model.StZvarlB; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * 描述: 库( 湖)容曲线表 * author: xusan * date: 2024-07-08 17:30:38 */ @Service @Slf4j @Transactional(rollbackFor = Exception.class) public class StZvarlBService extends ServiceImpl { /** * 根据水位查表得库容 * * @param rz 水位 * @return 查表库容 * @author lyf */ public BigDecimal getWFromZvarl(BigDecimal rz) { return getWFromZvarl(rz, BigDecimal.ZERO, list()); } /** * 根据水位查表得库容 * * @param rz 水位 * @param defaultValue 查不到时默认值 * @return 查表库容 * @author lyf */ public BigDecimal getWFromZvarl(BigDecimal rz, BigDecimal defaultValue) { return getWFromZvarl(rz, defaultValue, list()); } /** * 根据水位查表得库容 * * @param rz 水位 * @param defaultValue 查不到时默认值 * @param zvarlList 水位库容关系曲线 * @return 查表库容 * @author lyf */ public BigDecimal getWFromZvarl(BigDecimal rz, BigDecimal defaultValue, List zvarlList) { return toMap(zvarlList).getOrDefault(rz.setScale(3, RoundingMode.DOWN), defaultValue); } /** * 将水位库容曲线列表转为字典,水位作为键,注意BigDecimal做键的时候要匹配值和精度 * * @param zvarlList 水位库容曲线列表 * @return 水位库容曲线字典 * @author lyf */ private Map toMap(List zvarlList) { return zvarlList.stream() .map(item -> Map.entry(item.getRz().setScale(3, RoundingMode.DOWN), item.getW())) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } /** * 分段线性计算水位->库容 * 首先需要保证 水位->库容的数据要按水位从小到大进行排列 * @param zvarlBS * @param rz * @return */ public BigDecimal getWByZvarl(List zvarlBS, BigDecimal rz) { //对数据进行排序 // 1. 参数校验 if (zvarlBS == null || zvarlBS.isEmpty()) { throw new IllegalArgumentException("水位-库容关系列表不能为空"); } if (rz == null) { throw new IllegalArgumentException("水位值不能为空"); } // 2. 检查边界情况:如果目标水位低于最低水位或高于最高水位 BigDecimal minRz = zvarlBS.get(0).getRz(); BigDecimal maxRz = zvarlBS.get(zvarlBS.size() - 1).getRz(); if (rz.compareTo(minRz) < 0) { // 低于最低水位,可以选择抛出异常或进行外推(这里选择抛出异常) return BigDecimal.ZERO; } if (rz.compareTo(maxRz) > 0) { // 高于最高水位,可以选择抛出异常或进行外推(这里选择抛出异常) return BigDecimal.ZERO; } int l = 0; int r = zvarlBS.size() -1; //二分查找 由线性On时间复杂度降为Ologn while (l <= r) { int mid = l + (r-l) / 2; BigDecimal midRz = zvarlBS.get(mid).getRz(); int compareResult = midRz.compareTo(rz); if(compareResult == 0 ){ return zvarlBS.get(mid).getW(); }else if(compareResult < 0){ l = mid + 1; }else{ r = mid - 1; } } /** * 此时 l 指向第一个大于rz的位置 * r 指向最后一个小于rz的位置 * 既 r为 x1 y1 * l为 x2 y2 */ if(l < 0 || r >= zvarlBS.size()){ return BigDecimal.ZERO; } //获取前后差值的点 StZvarlB lowerPoint = zvarlBS.get(r); StZvarlB upperPoint = zvarlBS.get(l); return linearInterpolation(lowerPoint.getRz(),lowerPoint.getW(),upperPoint.getRz(),upperPoint.getW(),rz).setScale(3); } /** * 线性插值辅助方法 * @param x1 已知点1的水位 * @param y1 已知点1的库容 * @param x2 已知点2的水位 * @param y2 已知点2的库容 * @param x 目标水位 * @return 目标水位对应的库容 */ private BigDecimal linearInterpolation(BigDecimal x1, BigDecimal y1, BigDecimal x2, BigDecimal y2, BigDecimal x) { // 使用公式: y = y1 + ( (y2 - y1) / (x2 - x1) ) * (x - x1) // 计算斜率: (y2 - y1) / (x2 - x1) BigDecimal slope = y2.subtract(y1) .divide(x2.subtract(x1), 10, RoundingMode.HALF_UP); // 计算: slope * (x - x1) BigDecimal xDiff = x.subtract(x1); BigDecimal product = slope.multiply(xDiff); // 计算最终结果: y1 + product return y1.add(product); } }