气象降采样
This commit is contained in:
parent
b54df8b5f8
commit
20625bdcad
|
|
@ -0,0 +1,7 @@
|
|||
package org.jeecg.common.constant;
|
||||
|
||||
public class WeatherStepConstants {
|
||||
|
||||
|
||||
public static final double DEFAULT_STEP = 0.5;
|
||||
}
|
||||
|
|
@ -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<WeatherDataMapper, Weath
|
|||
}
|
||||
|
||||
/**
|
||||
* 根据类型和小时数获取天气数据
|
||||
* 气象预览
|
||||
* @param weatherId 气象数据id
|
||||
* @param weatherType 天气类型
|
||||
* @return 天气数据列表
|
||||
|
|
@ -669,11 +670,6 @@ public class WeatherDataServiceImpl extends ServiceImpl<WeatherDataMapper, Weath
|
|||
*/
|
||||
private WeatherResultVO processWeatherData(Integer weatherType, LocalDateTime targetTime, WeatherDataSourceEnum dataTypeEnum) {
|
||||
WeatherResultVO weatherResultVO = new WeatherResultVO();
|
||||
|
||||
if (WeatherTypeEnum.WIND.getKey().equals(weatherType)) {
|
||||
return processWindData(weatherType, targetTime, dataTypeEnum, weatherResultVO);
|
||||
}
|
||||
|
||||
String filePath = buildFilePath(targetTime, weatherType, dataTypeEnum);
|
||||
validateFile(filePath);
|
||||
|
||||
|
|
@ -682,9 +678,27 @@ public class WeatherDataServiceImpl extends ServiceImpl<WeatherDataMapper, Weath
|
|||
List<Double> latData = NcUtil.getNCList(ncFile, "lat");
|
||||
validateLonLatData(lonData, latData, filePath);
|
||||
|
||||
List<List<Double>> 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<List<List<Double>>> windDataList = processWindData(weatherType, targetTime, dataTypeEnum, weatherResultVO);
|
||||
processResultDataInternal(weatherResultVO, null, windDataList.get(0), windDataList.get(1), lonData, latData, converter);
|
||||
}else{
|
||||
List<List<Double>> 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<WeatherDataMapper, Weath
|
|||
/**
|
||||
* 处理风场数据
|
||||
*/
|
||||
private WeatherResultVO processWindData(Integer weatherType, LocalDateTime targetTime,
|
||||
private List<List<List<Double>>> processWindData(Integer weatherType, LocalDateTime targetTime,
|
||||
WeatherDataSourceEnum dataTypeEnum, WeatherResultVO weatherResultVO) {
|
||||
List<List<List<Double>>> 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<WeatherDataMapper, Weath
|
|||
WeatherVariableNameEnum.getValueByTypeAndKey(dataTypeEnum.getKey(), WeatherTypeEnum.WIND.getKey()), 0, 0);
|
||||
List<List<Double>> 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<WeatherDataMapper, Weath
|
|||
List<List<Double>> 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<WeatherDataMapper, Weath
|
|||
weatherResultVO.setDataList(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理天气类型数据
|
||||
*/
|
||||
private List<List<Double>> processWeatherTypeData(NetcdfFile ncFile, List<Double> lonData, List<Double> 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<List<Double>> processVariableData(NetcdfFile ncFile, List<Double> lonData, List<Double> latData,
|
||||
int dataType, WeatherTypeEnum weatherType, ValueConverter converter) {
|
||||
private List<List<Double>> processVariableData(NetcdfFile ncFile, int dataType, WeatherTypeEnum weatherType) {
|
||||
try {
|
||||
String variableName = WeatherVariableNameEnum.getValueByTypeAndKey(dataType, weatherType.getKey());
|
||||
List<List<Double>> 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<WeatherDataMapper, Weath
|
|||
/**
|
||||
* 处理结果数据
|
||||
*/
|
||||
private void processResultData(WeatherResultVO weatherResultVO, List<List<Double>> dataList,
|
||||
List<Double> lonData, List<Double> latData) {
|
||||
List<Double> flattenedList = new ArrayList<>();
|
||||
flattenList(dataList, flattenedList);
|
||||
List<Double> maxMin = getMaxMin(flattenedList);
|
||||
/**
|
||||
* 内部处理方法
|
||||
*/
|
||||
private void processResultDataInternal(WeatherResultVO weatherResultVO, List<List<Double>> dataList,
|
||||
List<List<Double>> uDataList, List<List<Double>> vDataList,
|
||||
List<Double> lonData, List<Double> 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<Double> maxMinLon = getMaxMin(lonData);
|
||||
List<Double> 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<Double> flattenedList = new ArrayList<>();
|
||||
flattenList(dataList, flattenedList);
|
||||
List<Double> 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<Double> flattenedUList = new ArrayList<>();
|
||||
List<Double> flattenedVList = new ArrayList<>();
|
||||
flattenList(uDataList, flattenedUList);
|
||||
flattenList(vDataList, flattenedVList);
|
||||
|
||||
// 风场的最大值最小值可以基于风速大小计算,或者分别处理U、V分量
|
||||
List<Double> maxMinU = getMaxMin(flattenedUList);
|
||||
List<Double> 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<List<Double>> resultWindData(List<List<Double>> uDataList, List<List<Double>> vDataList,
|
||||
List<Double> lonData, List<Double> latData, ValueConverter converter) {
|
||||
List<List<Double>> 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<List<Double>> resultWeatherData(List<List<Double>> dataList, List<Double> lonData,
|
||||
List<Double> latData, ValueConverter converter) {
|
||||
List<List<Double>> 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<Double> lonData, List<Double> 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<List<Double>> downsampleData(List<List<Double>> 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<List<Double>> downsampled = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < originalRows; i += ratio) {
|
||||
if (i >= originalData.size()) break;
|
||||
|
||||
List<Double> originalRow = originalData.get(i);
|
||||
List<Double> 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<Double> downsampleLonLat(List<Double> originalList, int ratio) {
|
||||
if (originalList == null || originalList.isEmpty() || ratio <= 1) {
|
||||
return originalList;
|
||||
}
|
||||
|
||||
List<Double> 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<List<Double>> dataList, List<Double> flattenedList) {
|
||||
for (List<Double> row : dataList) {
|
||||
flattenedList.addAll(row);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Double> getMaxMin(List<Double> 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<WeatherDataMapper, Weath
|
|||
return Files.exists(path) && !Files.isDirectory(path);
|
||||
}
|
||||
|
||||
private List<List<Double>> processWeatherData(List<List<Double>> dataList, List<Double> lonData,
|
||||
List<Double> latData, ValueConverter converter) {
|
||||
List<List<Double>> 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<List<Double>> nestedList, List<Double> flattenedList) {
|
||||
for (List<Double> subList : nestedList) {
|
||||
if (subList.size() >= 3) {
|
||||
flattenedList.add(subList.get(2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Double> getMaxMin(List<Double> 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)
|
||||
|
|
|
|||
|
|
@ -6,9 +6,19 @@ import java.util.List;
|
|||
|
||||
@Data
|
||||
public class WeatherResultVO {
|
||||
private List<List<Double>> 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<List<Double>> dataList;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user