diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/WeatherStepConstants.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/WeatherStepConstants.java new file mode 100644 index 0000000..70d1e68 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/WeatherStepConstants.java @@ -0,0 +1,7 @@ +package org.jeecg.common.constant; + +public class WeatherStepConstants { + + + public static final double DEFAULT_STEP = 0.5; +} diff --git a/jeecg-module-weather/src/main/java/org/jeecg/service/impl/WeatherDataServiceImpl.java b/jeecg-module-weather/src/main/java/org/jeecg/service/impl/WeatherDataServiceImpl.java index 467c96f..0058285 100644 --- a/jeecg-module-weather/src/main/java/org/jeecg/service/impl/WeatherDataServiceImpl.java +++ b/jeecg-module-weather/src/main/java/org/jeecg/service/impl/WeatherDataServiceImpl.java @@ -12,6 +12,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.jeecg.common.constant.WeatherPrefixConstants; +import org.jeecg.common.constant.WeatherStepConstants; import org.jeecg.common.constant.WeatherSuffixConstants; import org.jeecg.common.constant.enums.*; import org.jeecg.common.exception.JeecgBootException; @@ -94,7 +95,7 @@ public class WeatherDataServiceImpl extends ServiceImpl latData = NcUtil.getNCList(ncFile, "lat"); validateLonLatData(lonData, latData, filePath); - List> dataList = processWeatherTypeData(ncFile, lonData, latData, weatherType, dataTypeEnum.getKey()); - - processResultData(weatherResultVO, dataList, lonData, latData); + ValueConverter converter = null; + if (WeatherTypeEnum.WIND.getKey().equals(weatherType)) { + converter = value -> value; + List>> windDataList = processWindData(weatherType, targetTime, dataTypeEnum, weatherResultVO); + processResultDataInternal(weatherResultVO, null, windDataList.get(0), windDataList.get(1), lonData, latData, converter); + }else{ + List> dataList; + if (WeatherTypeEnum.TEMPERATURE.getKey().equals(weatherType)) { + converter = value -> value - 273.15; + dataList = processVariableData(ncFile, dataTypeEnum.getKey(), WeatherTypeEnum.TEMPERATURE); + } else if (WeatherTypeEnum.PRESSURE.getKey().equals(weatherType)) { + converter = value -> value / 1000; + dataList = processVariableData(ncFile, dataTypeEnum.getKey(), WeatherTypeEnum.PRESSURE); + } else if (WeatherTypeEnum.HUMIDITY.getKey().equals(weatherType)) { + converter = value -> value; + dataList = processVariableData(ncFile, dataTypeEnum.getKey(), WeatherTypeEnum.HUMIDITY); + } else { + throw new JeecgBootException("未知天气类型!"); + } + processResultDataInternal(weatherResultVO, dataList, null, null, lonData, latData, converter); + } } catch (IOException e) { log.error("NetCDF文件处理失败: {}", filePath, e); throw new JeecgBootException("文件读取失败", e); @@ -696,8 +710,9 @@ public class WeatherDataServiceImpl extends ServiceImpl>> processWindData(Integer weatherType, LocalDateTime targetTime, WeatherDataSourceEnum dataTypeEnum, WeatherResultVO weatherResultVO) { + List>> windDataList = new ArrayList<>(); if (!WeatherDataSourceEnum.CRA40.equals(dataTypeEnum)) { String filePath = buildFilePath(targetTime, weatherType, dataTypeEnum); validateFile(filePath); @@ -707,8 +722,8 @@ public class WeatherDataServiceImpl extends ServiceImpl> v = NcUtil.get2DNCByName(ncFile, WeatherVariableNameEnum.getValueByTypeAndKey(dataTypeEnum.getKey(), WeatherTypeEnum.WIND.getKey() + 1), 0, 0); - - setWindResult(weatherResultVO, u, v); + windDataList.add(u); + windDataList.add(v); } catch (IOException e) { log.error("NetCDF文件处理失败", e); throw new JeecgBootException("文件读取失败", e); @@ -727,13 +742,14 @@ public class WeatherDataServiceImpl extends ServiceImpl> v = NcUtil.get2DNCByName(vNcFile, WeatherVariableNameEnum.getValueByTypeAndKey(dataTypeEnum.getKey(), WeatherTypeEnum.WIND.getKey() + 1), 0, 0); - setWindResult(weatherResultVO, u, v); + windDataList.add(u); + windDataList.add(v); } catch (IOException e) { log.error("NetCDF文件处理失败", e); throw new JeecgBootException("文件读取失败", e); } } - return weatherResultVO; + return windDataList; } /** @@ -751,31 +767,13 @@ public class WeatherDataServiceImpl extends ServiceImpl> processWeatherTypeData(NetcdfFile ncFile, List lonData, List latData, - Integer weatherType, int dataType) { - if (WeatherTypeEnum.TEMPERATURE.getKey().equals(weatherType)) { - return processVariableData(ncFile, lonData, latData, dataType, WeatherTypeEnum.TEMPERATURE, value -> value - 273.15); - } else if (WeatherTypeEnum.PRESSURE.getKey().equals(weatherType)) { - return processVariableData(ncFile, lonData, latData, dataType, WeatherTypeEnum.PRESSURE, value -> value / 1000); - } else if (WeatherTypeEnum.HUMIDITY.getKey().equals(weatherType)) { - return processVariableData(ncFile, lonData, latData, dataType, WeatherTypeEnum.HUMIDITY, value -> value); - } else { - throw new JeecgBootException("未知天气类型!"); - } - } - /** * 处理变量数据 */ - private List> processVariableData(NetcdfFile ncFile, List lonData, List latData, - int dataType, WeatherTypeEnum weatherType, ValueConverter converter) { + private List> processVariableData(NetcdfFile ncFile, int dataType, WeatherTypeEnum weatherType) { try { String variableName = WeatherVariableNameEnum.getValueByTypeAndKey(dataType, weatherType.getKey()); - List> dataList = NcUtil.get2DNCByName(ncFile, variableName, 0, 0); - return processWeatherData(dataList, lonData, latData, converter); + return NcUtil.get2DNCByName(ncFile, variableName, 0, 0); } catch (Exception e) { log.error("处理{}数据失败", weatherType.name(), e); return Collections.emptyList(); @@ -850,17 +848,230 @@ public class WeatherDataServiceImpl extends ServiceImpl> dataList, - List lonData, List latData) { - List flattenedList = new ArrayList<>(); - flattenList(dataList, flattenedList); - List maxMin = getMaxMin(flattenedList); + /** + * 内部处理方法 + */ + private void processResultDataInternal(WeatherResultVO weatherResultVO, List> dataList, + List> uDataList, List> vDataList, + List lonData, List latData, ValueConverter converter) { + // 统一将采样率降为0.5 + double originalStep = getOriginalStep(lonData, latData); + double targetStep = WeatherStepConstants.DEFAULT_STEP; - weatherResultVO.setMax(maxMin.get(0)); - weatherResultVO.setMin(maxMin.get(1)); + if (originalStep < targetStep - 0.001) { // 添加容差避免浮点数比较问题 + // 需要降采样 + int ratio = calculateSafeRatio(originalStep, targetStep); + + // 根据数据类型进行降采样 + if (dataList != null) { + dataList = downsampleData(dataList, ratio); + } else if (uDataList != null && vDataList != null) { + uDataList = downsampleData(uDataList, ratio); + vDataList = downsampleData(vDataList, ratio); + } + + lonData = downsampleLonLat(lonData, ratio); + latData = downsampleLonLat(latData, ratio); + } + + List maxMinLon = getMaxMin(lonData); + List maxMinLat = getMaxMin(latData); + + weatherResultVO.setMaxLong(maxMinLon.get(0)); + weatherResultVO.setMinLong(maxMinLon.get(1)); + weatherResultVO.setMaxLat(maxMinLat.get(0)); + weatherResultVO.setMinLat(maxMinLat.get(1)); weatherResultVO.setSn(latData.size()); weatherResultVO.setWe(lonData.size()); - weatherResultVO.setDataList(dataList); + weatherResultVO.setXSpeed(targetStep); + weatherResultVO.setYSpeed(targetStep); + + // 根据数据类型选择不同的处理方法 + if (dataList != null) { + // 处理普通气象数据 + List flattenedList = new ArrayList<>(); + flattenList(dataList, flattenedList); + List maxMinValue = getMaxMin(flattenedList); + weatherResultVO.setMax(maxMinValue.get(0)); + weatherResultVO.setMin(maxMinValue.get(1)); + weatherResultVO.setDataList(resultWeatherData(dataList, lonData, latData, converter)); + } else if (uDataList != null && vDataList != null) { + // 处理风场数据 + List flattenedUList = new ArrayList<>(); + List flattenedVList = new ArrayList<>(); + flattenList(uDataList, flattenedUList); + flattenList(vDataList, flattenedVList); + + // 风场的最大值最小值可以基于风速大小计算,或者分别处理U、V分量 + List maxMinU = getMaxMin(flattenedUList); + List maxMinV = getMaxMin(flattenedVList); + weatherResultVO.setMaxU(maxMinU.get(0)); + weatherResultVO.setMinU(maxMinU.get(1)); + weatherResultVO.setMaxV(maxMinV.get(0)); + weatherResultVO.setMinV(maxMinV.get(1)); + weatherResultVO.setDataList(resultWindData(uDataList, vDataList, lonData, latData, converter)); + } + } + + /** + * 处理风场数据(只有U、V分量) + */ + private List> resultWindData(List> uDataList, List> vDataList, + List lonData, List latData, ValueConverter converter) { + List> results = new ArrayList<>(); + + for (int i = 0; i < latData.size(); i++) { + for (int j = 0; j < lonData.size(); j++) { + // 获取风场U、V分量 + double uValue = j < uDataList.get(i).size() ? uDataList.get(i).get(j) : uDataList.get(i).get(0); + double vValue = j < vDataList.get(i).size() ? vDataList.get(i).get(j) : vDataList.get(i).get(0); + + double roundedU = Math.round(converter.convert(uValue) * 1000.0) / 1000.0; + double roundedV = Math.round(converter.convert(vValue) * 1000.0) / 1000.0; + + // 风场数据格式:[经度, 纬度, U分量, V分量] + results.add(List.of(lonData.get(j), latData.get(i), roundedU, roundedV)); + } + } + return results; + } + + /** + * 处理普通气象数据(保持原有逻辑) + */ + private List> resultWeatherData(List> dataList, List lonData, + List latData, ValueConverter converter) { + List> results = new ArrayList<>(); + for (int i = 0; i < latData.size(); i++) { + for (int j = 0; j < lonData.size(); j++) { + double value = j < dataList.get(i).size() ? dataList.get(i).get(j) : dataList.get(i).get(0); + double roundedValue = Math.round(converter.convert(value) * 1000.0) / 1000.0; + results.add(List.of(lonData.get(j), latData.get(i), roundedValue)); + } + } + return results; + } + + /** + * 改进的步长计算方法,考虑经纬度点数 + */ + private double getOriginalStep(List lonData, List latData) { + if (lonData.size() < 2 || latData.size() < 2) { + return WeatherStepConstants.DEFAULT_STEP; + } + + // 方法1:直接计算相邻点的差值 + double directLonStep = Math.abs(lonData.get(1) - lonData.get(0)); + double directLatStep = Math.abs(latData.get(1) - latData.get(0)); + + // 方法2:根据点数估算步长(用于验证) + double estimatedLonStep = 360.0 / (lonData.size() - 1); + double estimatedLatStep = 180.0 / (latData.size() - 1); + + // 优先使用直接计算的结果,如果异常则使用估算值 + double lonStep = isValidStep(directLonStep) ? directLonStep : estimatedLonStep; + double latStep = isValidStep(directLatStep) ? directLatStep : estimatedLatStep; + + System.out.println("直接计算步长 - 经度: " + directLonStep + ", 纬度: " + directLatStep); + System.out.println("估算步长 - 经度: " + estimatedLonStep + ", 纬度: " + estimatedLatStep); + + return Math.min(lonStep, latStep); + } + + /** + * 验证步长是否合理 + */ + private boolean isValidStep(double step) { + return step > 0.01 && step < 10.0; // 合理的步长范围 + } + + /** + * 安全计算降采样比例,避免浮点数精度问题 + */ + private int calculateSafeRatio(double originalStep, double targetStep) { + // 常见分辨率的精确匹配 + if (Math.abs(originalStep - 0.1) < 0.001) { + return 5; // 0.1° -> 0.5°,比例5:1 + } else if (Math.abs(originalStep - 0.25) < 0.001) { + return 2; // 0.25° -> 0.5°,比例2:1 + } else if (Math.abs(originalStep - 0.5) < 0.001) { + return 1; // 0.5°,不降采样 + } else { + // 通用计算,四舍五入 + double ratio = targetStep / originalStep; + return (int) Math.round(ratio); + } + } + + /** + * 改进的二维数据降采样,处理边界情况 + */ + private List> downsampleData(List> originalData, int ratio) { + if (originalData == null || originalData.isEmpty() || ratio <= 1) { + return originalData; + } + + int originalRows = originalData.size(); + int originalCols = originalData.isEmpty() ? 0 : originalData.get(0).size(); + + List> downsampled = new ArrayList<>(); + + for (int i = 0; i < originalRows; i += ratio) { + if (i >= originalData.size()) break; + + List originalRow = originalData.get(i); + List newRow = new ArrayList<>(); + + for (int j = 0; j < originalCols; j += ratio) { + if (j < originalRow.size()) { + newRow.add(originalRow.get(j)); + } + } + + if (!newRow.isEmpty()) { + downsampled.add(newRow); + } + } + + System.out.println("数据降采样: " + originalRows + "x" + originalCols + " -> " + + downsampled.size() + "x" + (downsampled.isEmpty() ? 0 : downsampled.get(0).size())); + + return downsampled; + } + + /** + * 改进的经纬度降采样 + */ + private List downsampleLonLat(List originalList, int ratio) { + if (originalList == null || originalList.isEmpty() || ratio <= 1) { + return originalList; + } + + List downsampled = new ArrayList<>(); + + for (int i = 0; i < originalList.size(); i += ratio) { + downsampled.add(originalList.get(i)); + } + + System.out.println("经纬度降采样: " + originalList.size() + " -> " + downsampled.size()); + + return downsampled; + } + + // 原有的辅助方法保持不变 + private void flattenList(List> dataList, List flattenedList) { + for (List row : dataList) { + flattenedList.addAll(row); + } + } + + private List getMaxMin(List list) { + if (list == null || list.isEmpty()) { + return Arrays.asList(0.0, 0.0); + } + double max = Collections.max(list); + double min = Collections.min(list); + return Arrays.asList(max, min); } /** @@ -900,34 +1111,6 @@ public class WeatherDataServiceImpl extends ServiceImpl> processWeatherData(List> dataList, List lonData, - List latData, ValueConverter converter) { - List> results = new ArrayList<>(); - for (int i = 0; i < latData.size(); i++) { - for (int j = 0; j < lonData.size(); j++) { - double value = j < dataList.get(i).size() ? dataList.get(i).get(j) : dataList.get(i).get(0); - double roundedValue = Math.round(converter.convert(value) * 1000.0) / 1000.0; - results.add(List.of(lonData.get(j), latData.get(i), roundedValue)); - } - } - return results; - } - - public void flattenList(List> nestedList, List flattenedList) { - for (List subList : nestedList) { - if (subList.size() >= 3) { - flattenedList.add(subList.get(2)); - } - } - } - - public static List getMaxMin(List flattenedList) { - if (flattenedList == null || flattenedList.isEmpty()) { - return List.of(0.0, 0.0); - } - return List.of(Collections.max(flattenedList), Collections.min(flattenedList)); - } - /** * 根据经纬度获取网格下标 * @param latitude 纬度(-90到90) diff --git a/jeecg-module-weather/src/main/java/org/jeecg/vo/WeatherResultVO.java b/jeecg-module-weather/src/main/java/org/jeecg/vo/WeatherResultVO.java index 65c47fc..1d7e8f6 100644 --- a/jeecg-module-weather/src/main/java/org/jeecg/vo/WeatherResultVO.java +++ b/jeecg-module-weather/src/main/java/org/jeecg/vo/WeatherResultVO.java @@ -6,9 +6,19 @@ import java.util.List; @Data public class WeatherResultVO { - private List> dataList; private double max; private double min; + private double maxU; + private double minU; + private double maxV; + private double minV; + private double maxLat; + private double minLat; + private double maxLong; + private double minLong; private int sn; private int we; + private double xSpeed ; + private double ySpeed; + private List> dataList; }