玫瑰图,H事件地址
This commit is contained in:
parent
457da7d6aa
commit
6013997d31
|
|
@ -4,4 +4,24 @@ public class WeatherStepConstants {
|
|||
|
||||
|
||||
public static final double DEFAULT_STEP = 0.5;
|
||||
|
||||
/**
|
||||
* 16方向名称
|
||||
*/
|
||||
public static final String[] DIRECTIONS_16 = {
|
||||
"北", "北北东", "东北", "东北东",
|
||||
"东", "东南东", "东南", "东南南",
|
||||
"南", "西南南", "西南", "西南西",
|
||||
"西", "西北西", "西北", "西北北"
|
||||
};
|
||||
|
||||
/**
|
||||
* 16方向的角度范围(度)
|
||||
*/
|
||||
public static final double[] DIRECTION_ANGLES = {
|
||||
0, 22.5, 45, 67.5,
|
||||
90, 112.5, 135, 157.5,
|
||||
180, 202.5, 225, 247.5,
|
||||
270, 292.5, 315, 337.5
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
package org.jeecg.common.util;
|
||||
|
||||
import lombok.val;
|
||||
import ucar.ma2.Array;
|
||||
import ucar.ma2.ArrayDouble;
|
||||
import ucar.ma2.DataType;
|
||||
import ucar.ma2.Index;
|
||||
import ucar.ma2.*;
|
||||
import ucar.nc2.Attribute;
|
||||
import ucar.nc2.NetcdfFile;
|
||||
import ucar.nc2.NetcdfFileWriter;
|
||||
|
|
@ -46,6 +43,48 @@ public final class NcUtil {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据经纬度下标取数据
|
||||
*/
|
||||
public static Double getNcValueByIndex(NetcdfFile ncfile, String name, int layer, Integer hour, int latSize, int lonSize) {
|
||||
Objects.requireNonNull(ncfile, "NetcdfFile cannot be null");
|
||||
Objects.requireNonNull(name, "Variable name cannot be null");
|
||||
|
||||
try {
|
||||
Variable variable = ncfile.findVariable(name);
|
||||
if (variable == null) {
|
||||
throw new IllegalArgumentException("Variable '" + name + "' not found in NetCDF file");
|
||||
}
|
||||
|
||||
// 根据变量的维度数创建合适的origin数组
|
||||
int[] origin;
|
||||
int[] section;
|
||||
|
||||
// 假设变量有4个维度:(time, layer, lat, lon)
|
||||
if (variable.getRank() == 4) {
|
||||
origin = new int[]{hour, layer, latSize, lonSize};
|
||||
section = new int[]{1, 1, 1, 1};
|
||||
}
|
||||
// 假设变量有3个维度:(time, lat, lon) 或 (layer, lat, lon)
|
||||
else if (variable.getRank() == 3) {
|
||||
origin = new int[]{hour, latSize, lonSize};
|
||||
section = new int[]{1, 1, 1};
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported variable rank: " + variable.getRank());
|
||||
}
|
||||
|
||||
Array data = variable.read(origin, section);
|
||||
Index index = data.getIndex();
|
||||
double value = data.getDouble(index);
|
||||
return Math.round(value * 1000.0) / 1000.0;
|
||||
} catch (IOException e) {
|
||||
logger.error("Error reading variable {} from NetCDF file", name, e);
|
||||
throw new RuntimeException("Failed to read NetCDF data", e);
|
||||
} catch (InvalidRangeException e) {
|
||||
throw new RuntimeException("Invalid range specified for variable reading", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取三维数据,如果为四维数据,层级固定读取第一层数据,降为三维数据
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package org.jeecg.baseAPI.service.impl;
|
|||
import lombok.RequiredArgsConstructor;
|
||||
import org.jeecg.baseAPI.service.BaseAPIService;
|
||||
import org.jeecg.common.properties.DiffusionProperties;
|
||||
import org.jeecg.common.properties.EventServerProperties;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
|
|
@ -21,7 +22,7 @@ import static java.lang.Math.toRadians;
|
|||
@RequiredArgsConstructor
|
||||
public class BaseAPIServiceImpl implements BaseAPIService {
|
||||
|
||||
private final DiffusionProperties diffusionProperties;
|
||||
private final EventServerProperties eventServerProperties;
|
||||
|
||||
@Override
|
||||
public String calcAngle(Double centerLon, Double centerLat, Double targetLon, Double targetLat,Double u,Double v) {
|
||||
|
|
@ -153,7 +154,7 @@ public class BaseAPIServiceImpl implements BaseAPIService {
|
|||
|
||||
@Override
|
||||
public String buildEngineeringFilePath(String modelPath, String createBy, String engineeringName) {
|
||||
return diffusionProperties.getDiffusionPath() +
|
||||
return eventServerProperties.getBaseHome() +
|
||||
File.separator +
|
||||
modelPath +
|
||||
File.separator +
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import org.jeecg.common.aspect.annotation.AutoLog;
|
|||
import org.jeecg.common.constant.DiffusionPrefixConstants;
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
import org.jeecg.common.properties.DiffusionProperties;
|
||||
import org.jeecg.common.properties.EventServerProperties;
|
||||
import org.jeecg.common.properties.SystemStorageProperties;
|
||||
import org.jeecg.common.util.NcUtil;
|
||||
import org.jeecg.diffusion.service.DiffusionDataService;
|
||||
|
|
@ -41,7 +42,7 @@ import java.util.List;
|
|||
@RequiredArgsConstructor
|
||||
public class DiffusionDataServiceImpl implements DiffusionDataService {
|
||||
|
||||
private final DiffusionProperties diffusionProperties;
|
||||
private final EventServerProperties eventServerProperties;
|
||||
private final BaseAPIService baseAPIService;
|
||||
private final WrfMapper wrfMapper;
|
||||
private final CmaqMapper cmaqMapper;
|
||||
|
|
@ -51,14 +52,13 @@ public class DiffusionDataServiceImpl implements DiffusionDataService {
|
|||
public DiffusionResultVO getDiffusionResult(String enginId, int hour, int layer) {
|
||||
Engineering engineering = engineeringMapper.selectById(enginId);
|
||||
Wrf wrf = wrfMapper.selectOne(new LambdaQueryWrapper<Wrf>().eq(Wrf::getEnginId,enginId));
|
||||
String wrfFilePath = baseAPIService.buildEngineeringFilePath(diffusionProperties.getWrfPath() ,engineering.getCreateBy(), engineering.getEngineeringName());
|
||||
String cmaqFilePath = baseAPIService.buildEngineeringFilePath(diffusionProperties.getWrfPath() ,engineering.getCreateBy(), engineering.getEngineeringName());
|
||||
String wrfFilePath = baseAPIService.buildEngineeringFilePath(eventServerProperties.getResultFilePrefix() ,engineering.getCreateBy(), engineering.getEngineeringName());
|
||||
String cmaqFilePath = baseAPIService.buildEngineeringFilePath(eventServerProperties.getResultFilePrefix() ,engineering.getCreateBy(), engineering.getEngineeringName());
|
||||
|
||||
|
||||
try (NetcdfFile wrfNcFile = getWrfNetcdfFile(wrfFilePath, wrf.getStartTime());
|
||||
NetcdfFile cmaqNcFile = getCmaqNetcdfFile(cmaqFilePath, wrf.getStartTime());){
|
||||
DiffusionResultVO diffusionResultVO = new DiffusionResultVO();
|
||||
List<List<List<Double>>> dataList = new ArrayList<>();
|
||||
|
||||
List<List<Double>> values = NcUtil.get2DNCByName(cmaqNcFile, "CO", layer, hour);
|
||||
List<List<Double>> xlats = NcUtil.get2DNCByName(wrfNcFile, "XLAT", layer, hour);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package org.jeecg.runProcess.service.impl;
|
|||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jeecg.baseAPI.service.BaseAPIService;
|
||||
import org.jeecg.cmaq.service.CmaqService;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.properties.EventServerProperties;
|
||||
|
|
@ -28,6 +29,7 @@ public class RunProcessServiceImpl implements RunProcessService {
|
|||
private final WrfService wrfService;
|
||||
private final CmaqService cmaqService;
|
||||
private final EngineeringService engineeringService;
|
||||
private final BaseAPIService baseAPIService;
|
||||
private final EventServerProperties eventServerProperties;
|
||||
|
||||
@Override
|
||||
|
|
@ -38,9 +40,8 @@ public class RunProcessServiceImpl implements RunProcessService {
|
|||
Wrf wrf = wrfService.getOne(new LambdaQueryWrapper<Wrf>().eq(Wrf::getEnginId,engineeringId));
|
||||
|
||||
//各种路径前缀
|
||||
String allRunPath = eventServerProperties.getBaseHome() + File.separator + engineering.getCreateBy() + File.separator + engineering.getEngineeringName() + File.separator;
|
||||
String resultFilePath = eventServerProperties.getBaseHome() + File.separator + eventServerProperties.getResultFilePrefix() +
|
||||
File.separator + engineering.getCreateBy() + File.separator + engineering.getEngineeringName() + File.separator;
|
||||
String allRunPath = baseAPIService.buildEngineeringFilePath("" ,engineering.getCreateBy(), engineering.getEngineeringName());
|
||||
String resultFilePath = baseAPIService.buildEngineeringFilePath(eventServerProperties.getResultFilePrefix() ,engineering.getCreateBy(), engineering.getEngineeringName());
|
||||
String workDirPath = allRunPath + "workdir/";
|
||||
String mcipPath = allRunPath + "workdir/MCIP/";
|
||||
String scriptsPath = allRunPath + "scripts/";
|
||||
|
|
|
|||
|
|
@ -40,13 +40,13 @@ public class WrfController extends JeecgController<Wrf, WrfService> {
|
|||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
* 查询wrf参数信息
|
||||
*
|
||||
* @param enginId
|
||||
* @return
|
||||
*/
|
||||
@AutoLog(value = "编辑")
|
||||
@Operation(summary = "编辑")
|
||||
@AutoLog(value = "查询wrf参数信息")
|
||||
@Operation(summary = "查询wrf参数信息")
|
||||
@RequestMapping(value = "/getByEnginId", method = {RequestMethod.GET})
|
||||
public Result<Wrf> getByEnginId(String enginId) {
|
||||
return Result.OK(wrfService.getOne(new LambdaQueryWrapper<Wrf>().eq(Wrf::getEnginId,enginId)));
|
||||
|
|
|
|||
|
|
@ -101,10 +101,29 @@ public class WeatherDataController {
|
|||
@AutoLog(value = "气象预测-气象折线图")
|
||||
@Operation(summary = "气象预测-气象折线图")
|
||||
@GetMapping(value = "getDataLine")
|
||||
public Result<?> getDataLine(Integer dataType,@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime startTime,@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime endTime, double longitude, double latitude) {
|
||||
public Result<?> getDataLine(Integer dataType,
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime startTime,
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime endTime,
|
||||
double longitude,
|
||||
double latitude) {
|
||||
return Result.OK(weatherDataService.getDataLine(dataType,startTime,endTime,longitude,latitude));
|
||||
}
|
||||
|
||||
/**
|
||||
* 风场玫瑰图
|
||||
* @return
|
||||
*/
|
||||
@AutoLog(value = "气象预测-风场玫瑰图")
|
||||
@Operation(summary = "气象预测-风场玫瑰图")
|
||||
@GetMapping(value = "getWindRose")
|
||||
public Result<?> getWindRose(Integer dataType,
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime startTime,
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime endTime,
|
||||
double longitude,
|
||||
double latitude) {
|
||||
return Result.OK(weatherDataService.getWindRose(dataType,startTime,endTime,longitude,latitude));
|
||||
}
|
||||
|
||||
@AutoLog(value = "删除气象数据")
|
||||
@Operation(summary = "删除气象数据")
|
||||
@DeleteMapping("delete")
|
||||
|
|
|
|||
|
|
@ -5,11 +5,9 @@ import com.baomidou.mybatisplus.extension.service.IService;
|
|||
import org.jeecg.common.system.query.PageRequest;
|
||||
import org.jeecg.modules.base.entity.StasDataSource;
|
||||
import org.jeecg.modules.base.entity.WeatherData;
|
||||
import org.jeecg.vo.FileExistVo;
|
||||
import org.jeecg.vo.FileUploadResultVo;
|
||||
import org.jeecg.vo.FileVo;
|
||||
import org.jeecg.vo.WeatherResultVO;
|
||||
import org.jeecg.vo.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
|
@ -19,7 +17,8 @@ public interface WeatherDataService extends IService<WeatherData> {
|
|||
|
||||
WeatherResultVO getWeatherData(Integer dataType, Integer weatherType, LocalDateTime startTime, int hour);
|
||||
WeatherResultVO getWeatherDataPreview(String weatherId, Integer weatherType);
|
||||
Map<String, List<String>> getDataLine(Integer dataType, LocalDateTime startTime, LocalDateTime endTime, double longitude, double latitude);
|
||||
WindDataLineVO getDataLine(Integer dataType, LocalDateTime startTime, LocalDateTime endTime, double longitude, double latitude);
|
||||
List<WindRoseData> getWindRose(Integer dataType, LocalDateTime startTime, LocalDateTime endTime,double longitude, double latitude);
|
||||
|
||||
/**
|
||||
* 分页查询气象数据
|
||||
|
|
|
|||
|
|
@ -24,10 +24,8 @@ import org.jeecg.common.util.NcUtil;
|
|||
import org.jeecg.modules.base.entity.WeatherData;
|
||||
import org.jeecg.modules.base.mapper.WeatherDataMapper;
|
||||
import org.jeecg.service.WeatherDataService;
|
||||
import org.jeecg.vo.FileExistVo;
|
||||
import org.jeecg.vo.FileUploadResultVo;
|
||||
import org.jeecg.vo.FileVo;
|
||||
import org.jeecg.vo.WeatherResultVO;
|
||||
import org.jeecg.utils.WindRoseDataGenerator;
|
||||
import org.jeecg.vo.*;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
|
@ -137,7 +135,7 @@ public class WeatherDataServiceImpl extends ServiceImpl<WeatherDataMapper, Weath
|
|||
* @return 天气数据列表
|
||||
*/
|
||||
@Override
|
||||
public Map<String, List<String>> getDataLine(Integer dataType, LocalDateTime startTime, LocalDateTime endTime,
|
||||
public WindDataLineVO getDataLine(Integer dataType, LocalDateTime startTime, LocalDateTime endTime,
|
||||
double longitude, double latitude) {
|
||||
|
||||
// 参数校验
|
||||
|
|
@ -150,7 +148,7 @@ public class WeatherDataServiceImpl extends ServiceImpl<WeatherDataMapper, Weath
|
|||
throw new IllegalArgumentException("时间范围内没有气象数据");
|
||||
}
|
||||
// 初始化结果集
|
||||
Map<String, List<String>> result = new LinkedHashMap<>(); // 保持插入顺序
|
||||
WindDataLineVO windDataLineVO = new WindDataLineVO();
|
||||
List<String> timeList = new ArrayList<>();
|
||||
List<String> temperatureList = new ArrayList<>();
|
||||
List<String> pressureList = new ArrayList<>();
|
||||
|
|
@ -180,13 +178,117 @@ public class WeatherDataServiceImpl extends ServiceImpl<WeatherDataMapper, Weath
|
|||
}
|
||||
|
||||
// 构建结果
|
||||
result.put("time", timeList);
|
||||
result.put("temperature", temperatureList);
|
||||
result.put("pressure", pressureList);
|
||||
result.put("humidity", humidityList);
|
||||
result.put("windSpeed", windSpeedList);
|
||||
windDataLineVO.setTime(timeList);
|
||||
windDataLineVO.setTemperature(temperatureList);
|
||||
windDataLineVO.setPressure(pressureList);
|
||||
windDataLineVO.setHumidity(humidityList);
|
||||
windDataLineVO.setWindSpeed(windSpeedList);
|
||||
analyzeWithStream(windSpeedList, windDataLineVO);
|
||||
|
||||
return result;
|
||||
return windDataLineVO;
|
||||
}
|
||||
|
||||
public void analyzeWithStream(List<String> windSpeedStrings, WindDataLineVO windDataLineVO) {
|
||||
if (windSpeedStrings == null || windSpeedStrings.isEmpty()) {
|
||||
throw new IllegalArgumentException("风速数据不能为空");
|
||||
}
|
||||
|
||||
// 使用Stream将String转换为double并获取统计量
|
||||
DoubleSummaryStatistics basicStats = windSpeedStrings.stream()
|
||||
.mapToDouble(Double::parseDouble) // String转double
|
||||
.summaryStatistics();
|
||||
|
||||
int count = windSpeedStrings.size();
|
||||
double mean = basicStats.getAverage();
|
||||
double max = basicStats.getMax();
|
||||
double min = basicStats.getMin();
|
||||
|
||||
// 计算标准差需要平均值,所以需要重新转换计算
|
||||
List<Double> windSpeeds = windSpeedStrings.stream()
|
||||
.map(Double::parseDouble)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
double variance = windSpeeds.stream()
|
||||
.mapToDouble(Double::doubleValue)
|
||||
.map(speed -> Math.pow(speed - mean, 2))
|
||||
.sum() / (count - 1);
|
||||
|
||||
double stdDev = Math.sqrt(variance);
|
||||
|
||||
// 创建格式化对象,保留两位小数
|
||||
DecimalFormat df = new DecimalFormat("#0.00");
|
||||
windDataLineVO.setMean(df.format(mean)); // 平均风速
|
||||
windDataLineVO.setMax(df.format(max)); // 最大风速
|
||||
windDataLineVO.setMin(df.format(min)); // 最小风速
|
||||
windDataLineVO.setStdDev(df.format(stdDev)); // 风速标准差
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<WindRoseData> getWindRose(Integer dataType, LocalDateTime startTime, LocalDateTime endTime,
|
||||
double longitude, double latitude) {
|
||||
// 参数校验
|
||||
if (startTime == null || endTime == null || startTime.isAfter(endTime)) {
|
||||
throw new IllegalArgumentException("时间参数无效");
|
||||
}
|
||||
|
||||
List<WeatherData> weatherDataList = weatherDataMapper.selectList(
|
||||
new LambdaQueryWrapper<WeatherData>()
|
||||
.between(WeatherData::getDataStartTime, startTime, endTime)
|
||||
.eq(WeatherData::getDataSource, dataType)
|
||||
);
|
||||
|
||||
if (weatherDataList == null || weatherDataList.isEmpty()) {
|
||||
throw new IllegalArgumentException("时间范围内没有气象数据");
|
||||
}
|
||||
|
||||
int[] gridIndex = getGridIndex(latitude, longitude);
|
||||
Map<String, String> variables = getVariableNames(dataType);
|
||||
List<Double> uValues = new ArrayList<>();
|
||||
List<Double> vValues = new ArrayList<>();
|
||||
|
||||
// 处理每个时间点数据
|
||||
LocalDateTime currentTime = startTime;
|
||||
while (!currentTime.isAfter(endTime)) {
|
||||
try {
|
||||
if (WeatherDataSourceEnum.CRA40.getKey().equals(dataType)) {
|
||||
// CRA40数据源:U、V分量在不同文件
|
||||
String uFilePath = getWeatherFilePath(weatherDataList, currentTime,
|
||||
WeatherTypeEnum.WIND.getKey(), WeatherDataSourceEnum.getInfoByKey(dataType));
|
||||
String vFilePath = getWeatherFilePath(weatherDataList, currentTime,
|
||||
WeatherTypeEnum.WIND.getKey() + 1, WeatherDataSourceEnum.getInfoByKey(dataType));
|
||||
|
||||
if (isFileValid(uFilePath) && isFileValid(vFilePath)) {
|
||||
try (NetcdfFile uNcFile = NetcdfFile.open(uFilePath);
|
||||
NetcdfFile vNcFile = NetcdfFile.open(vFilePath)) {
|
||||
uValues.add(getNcValueByIndex(uNcFile, variables.get("windU"), gridIndex[0], gridIndex[1]));
|
||||
vValues.add(getNcValueByIndex(vNcFile, variables.get("windV"), gridIndex[0], gridIndex[1]));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 标准数据源:U、V分量在同一文件
|
||||
String filePath = getWeatherFilePath(weatherDataList, currentTime,
|
||||
WeatherTypeEnum.WIND.getKey(), WeatherDataSourceEnum.getInfoByKey(dataType));
|
||||
|
||||
if (isFileValid(filePath)) {
|
||||
try (NetcdfFile ncFile = NetcdfFile.open(filePath)) {
|
||||
uValues.add(getNcValueByIndex(ncFile, variables.get("windU"), gridIndex[0], gridIndex[1]));
|
||||
vValues.add(getNcValueByIndex(ncFile, variables.get("windV"), gridIndex[0], gridIndex[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("处理{}时刻数据失败: {}", currentTime, e.getMessage());
|
||||
// 跳过该时间点,继续处理下一个
|
||||
}
|
||||
currentTime = currentTime.plusHours(6);
|
||||
}
|
||||
|
||||
// 验证数据有效性
|
||||
if (uValues.isEmpty() || vValues.isEmpty()) {
|
||||
throw new JeecgBootException("未读取到有效的风场数据");
|
||||
}
|
||||
|
||||
return WindRoseDataGenerator.generateWindRoseData16(uValues, vValues);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -540,16 +642,15 @@ public class WeatherDataServiceImpl extends ServiceImpl<WeatherDataMapper, Weath
|
|||
Map<String, String> variables = getVariableNames(dataType);
|
||||
if(WeatherDataSourceEnum.CRA40.getKey() != dataType){
|
||||
try (NetcdfFile ncFile = NetcdfFile.open(filePath)) {
|
||||
// 读取数据(使用通用NcUtil方法)
|
||||
List<List<Double>> tData = getVariableData(ncFile, variables.get("temperature"));
|
||||
List<List<Double>> pData = getVariableData(ncFile, variables.get("pressure"));
|
||||
List<List<Double>> hData = getVariableData(ncFile, variables.get("humidity"));
|
||||
List<List<Double>> uData = getVariableData(ncFile, variables.get("windU"));
|
||||
List<List<Double>> vData = getVariableData(ncFile, variables.get("windV"));
|
||||
|
||||
// 数据处理逻辑(与第一个方法保持一致)
|
||||
processAndAddData(gridIndex, currentTime, timeList, temperatureList, pressureList,
|
||||
humidityList, windSpeedList, tData, pData, hData, uData, vData);
|
||||
Double tValue = getNcValueByIndex(ncFile, variables.get("temperature"), gridIndex[0], gridIndex[1]) - 273.15;
|
||||
Double pValue = getNcValueByIndex(ncFile, variables.get("pressure"), gridIndex[0], gridIndex[1]) / 1000;
|
||||
Double hValue = getNcValueByIndex(ncFile, variables.get("humidity"), gridIndex[0], gridIndex[1]);
|
||||
Double uValue = getNcValueByIndex(ncFile, variables.get("windU"), gridIndex[0], gridIndex[1]);
|
||||
Double vValue = getNcValueByIndex(ncFile, variables.get("windV"), gridIndex[0], gridIndex[1]);
|
||||
Double windSpeed = Math.sqrt(uValue * uValue + vValue * vValue);
|
||||
// 添加格式化数据
|
||||
addFormattedData(currentTime, timeList, temperatureList, pressureList,
|
||||
humidityList, windSpeedList, tValue, pValue, hValue, windSpeed);
|
||||
}
|
||||
}else {
|
||||
// 循环处理每个时间点的数据
|
||||
|
|
@ -560,40 +661,37 @@ public class WeatherDataServiceImpl extends ServiceImpl<WeatherDataMapper, Weath
|
|||
String uFilePath = getWeatherFilePath(weatherDataList, currentTime, WeatherTypeEnum.WIND.getKey(), WeatherDataSourceEnum.getInfoByKey(dataType));
|
||||
String vFilePath = getWeatherFilePath(weatherDataList, currentTime,WeatherTypeEnum.WIND.getKey() + 1, WeatherDataSourceEnum.getInfoByKey(dataType));
|
||||
|
||||
List<List<Double>> tData = null;
|
||||
List<List<Double>> pData = null;
|
||||
List<List<Double>> hData = null;
|
||||
List<List<Double>> uData = null;
|
||||
List<List<Double>> vData = null;
|
||||
Double tValue = null;
|
||||
Double pValue = null;
|
||||
Double hValue = null;
|
||||
Double windSpeed = null;
|
||||
if (isFileValid(tFilePath)) {
|
||||
NetcdfFile ncFile = NetcdfFile.open(tFilePath);
|
||||
tData = getVariableData(ncFile, variables.get("temperature"));
|
||||
tValue = getNcValueByIndex(ncFile, variables.get("temperature"), gridIndex[0], gridIndex[1]) - 273.15;
|
||||
}
|
||||
if (isFileValid(pFilePath)) {
|
||||
NetcdfFile ncFile = NetcdfFile.open(pFilePath);
|
||||
pData = getVariableData(ncFile, variables.get("pressure"));
|
||||
pValue = getNcValueByIndex(ncFile, variables.get("pressure"), gridIndex[0], gridIndex[1]) / 1000;
|
||||
}
|
||||
if (isFileValid(hFilePath)) {
|
||||
NetcdfFile ncFile = NetcdfFile.open(hFilePath);
|
||||
hData = getVariableData(ncFile, variables.get("humidity"));
|
||||
hValue = getNcValueByIndex(ncFile, variables.get("humidity"), gridIndex[0], gridIndex[1]);
|
||||
}
|
||||
if (isFileValid(uFilePath) && isFileValid(vFilePath)) {
|
||||
NetcdfFile uNcFile = NetcdfFile.open(uFilePath);
|
||||
NetcdfFile vNcFile = NetcdfFile.open(vFilePath);
|
||||
uData = getVariableData(uNcFile, variables.get("windU"));
|
||||
vData = getVariableData(vNcFile, variables.get("windV"));
|
||||
Double uValue = getNcValueByIndex(uNcFile, variables.get("windU"), gridIndex[0], gridIndex[1]);
|
||||
Double vValue = getNcValueByIndex(vNcFile, variables.get("windV"), gridIndex[0], gridIndex[1]);
|
||||
windSpeed = Math.sqrt(uValue * uValue + vValue * vValue);
|
||||
}
|
||||
|
||||
// 数据处理逻辑(与第一个方法保持一致)
|
||||
processAndAddData(gridIndex, currentTime, timeList, temperatureList, pressureList,
|
||||
humidityList, windSpeedList, tData, pData, hData, uData, vData);
|
||||
// 添加格式化数据
|
||||
addFormattedData(currentTime, timeList, temperatureList, pressureList,
|
||||
humidityList, windSpeedList, tValue, pValue, hValue, windSpeed);
|
||||
}catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new JeecgBootException("处理气象数据失败!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -632,76 +730,8 @@ public class WeatherDataServiceImpl extends ServiceImpl<WeatherDataMapper, Weath
|
|||
/**
|
||||
* 通用数据获取方法
|
||||
*/
|
||||
private List<List<Double>> getVariableData(NetcdfFile ncFile, String variableName) {
|
||||
return NcUtil.get2DNCByName(ncFile, variableName, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据处理与添加(增强安全性版本)
|
||||
*/
|
||||
private void processAndAddData(int[] gridIndex, LocalDateTime currentTime,
|
||||
List<String> timeList, List<String> temperatureList,
|
||||
List<String> pressureList, List<String> humidityList,
|
||||
List<String> windSpeedList, List<List<Double>> tData,
|
||||
List<List<Double>> pData, List<List<Double>> hData,
|
||||
List<List<Double>> uData, List<List<Double>> vData) {
|
||||
|
||||
// 1. 参数基础校验
|
||||
Objects.requireNonNull(currentTime, "currentTime不能为null");
|
||||
validateLists(timeList, temperatureList, pressureList, humidityList, windSpeedList);
|
||||
|
||||
Double tValue = null;
|
||||
Double pValue = null;
|
||||
Double hValue = null;
|
||||
Double windSpeed = null;
|
||||
// 2. 安全获取数据(带默认值)
|
||||
if(null != tData && !tData.isEmpty()){
|
||||
tValue = getSafeGridValue(tData, gridIndex, 0.0) - 273.15;
|
||||
}
|
||||
if(null != tData && !tData.isEmpty()){
|
||||
pValue = getSafeGridValue(pData, gridIndex, 0.0) / 1000;
|
||||
}
|
||||
if(null != tData && !tData.isEmpty()){
|
||||
hValue = getSafeGridValue(hData, gridIndex, 0.0);
|
||||
}
|
||||
if(null != uData && !uData.isEmpty() && null != vData && !vData.isEmpty()){
|
||||
windSpeed = calculateWindSpeed(uData, vData, gridIndex);
|
||||
}
|
||||
|
||||
// 3. 添加格式化数据
|
||||
addFormattedData(currentTime, timeList, temperatureList, pressureList,
|
||||
humidityList, windSpeedList, tValue, pValue, hValue, windSpeed);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验List参数非空
|
||||
*/
|
||||
private void validateLists(List<?>... lists) {
|
||||
for (List<?> list : lists) {
|
||||
Objects.requireNonNull(list, "数据列表不能为null");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全获取网格数据(带默认值)
|
||||
*/
|
||||
private Double getSafeGridValue(List<List<Double>> gridData, int[] gridIndex, Double defaultValue) {
|
||||
try {
|
||||
return Optional.ofNullable(gridData.get(gridIndex[0]))
|
||||
.map(row -> row.get(gridIndex[1]))
|
||||
.orElse(defaultValue);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 增强版风速计算
|
||||
*/
|
||||
private double calculateWindSpeed(List<List<Double>> uData, List<List<Double>> vData, int[] gridIndex) {
|
||||
Double uValue = getSafeGridValue(uData, gridIndex, 0.0);
|
||||
Double vValue = getSafeGridValue(vData, gridIndex, 0.0);
|
||||
return Math.sqrt(uValue * uValue + vValue * vValue);
|
||||
private Double getNcValueByIndex(NetcdfFile ncFile, String variableName, int latSize, int lonSize) {
|
||||
return NcUtil.getNcValueByIndex(ncFile, variableName, 0, 0, latSize, lonSize);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -809,21 +839,6 @@ public class WeatherDataServiceImpl extends ServiceImpl<WeatherDataMapper, Weath
|
|||
return windDataList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置风场结果
|
||||
*/
|
||||
private void setWindResult(WeatherResultVO weatherResultVO, List<List<Double>> u, List<List<Double>> v) {
|
||||
List<List<Double>> results = new ArrayList<>();
|
||||
for (int i = 0; i < u.size(); i++) {
|
||||
for (int j = 0; j < u.get(0).size(); j++) {
|
||||
results.add(List.of(u.get(i).get(j), v.get(i).get(j)));
|
||||
}
|
||||
}
|
||||
weatherResultVO.setSn(u.size());
|
||||
weatherResultVO.setWe(u.get(0).size());
|
||||
weatherResultVO.setDataList(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理变量数据
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
package org.jeecg.utils;
|
||||
|
||||
import org.jeecg.common.constant.WeatherStepConstants;
|
||||
import org.jeecg.vo.WindRoseData;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class WindRoseDataGenerator {
|
||||
|
||||
/**
|
||||
* 生成16方向风场玫瑰图数据(只统计频次)
|
||||
*
|
||||
* @param uList U分量风速列表(东西方向)
|
||||
* @param vList V分量风速列表(南北方向)
|
||||
* @return 16个方向的风场数据列表
|
||||
*/
|
||||
public static List<WindRoseData> generateWindRoseData16(
|
||||
List<Double> uList,
|
||||
List<Double> vList) {
|
||||
|
||||
// 验证输入数据
|
||||
validateInputData(uList, vList);
|
||||
|
||||
// 初始化16个方向的数据容器(只需要计数,不需要存储具体风速值)
|
||||
int[] directionCounts = new int[16];
|
||||
int totalPoints = 0;
|
||||
|
||||
// 处理每个数据点
|
||||
for (int i = 0; i < uList.size(); i++) {
|
||||
Double u = uList.get(i);
|
||||
Double v = vList.get(i);
|
||||
|
||||
// 跳过无效数据
|
||||
if (u == null || v == null || Double.isNaN(u) || Double.isNaN(v)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 计算风向
|
||||
double direction = calculateWindDirection(u, v);
|
||||
|
||||
// 确定风向属于哪个方向区间并计数
|
||||
int dirIndex = getDirectionIndex(direction);
|
||||
directionCounts[dirIndex]++;
|
||||
totalPoints++;
|
||||
}
|
||||
|
||||
// 计算每个方向的统计信息
|
||||
List<WindRoseData> result = new ArrayList<>();
|
||||
for (int i = 0; i < 16; i++) {
|
||||
WindRoseData data = new WindRoseData(WeatherStepConstants.DIRECTIONS_16[i], WeatherStepConstants.DIRECTION_ANGLES[i]);
|
||||
data.setCount(directionCounts[i]);
|
||||
// 计算频率并保留两位小数
|
||||
double frequency = totalPoints > 0 ? (directionCounts[i] * 100.0 / totalPoints) : 0;
|
||||
double roundedFrequency = Math.round(frequency * 100.0) / 100.0;
|
||||
data.setFrequency(roundedFrequency);
|
||||
result.add(data);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算风向角度(0-360度,正北为0度,顺时针增加)
|
||||
*/
|
||||
private static double calculateWindDirection(double u, double v) {
|
||||
// 计算角度(弧度)
|
||||
double angleRad = Math.atan2(-u, -v);
|
||||
|
||||
// 转换为度(0-360)
|
||||
double angleDeg = Math.toDegrees(angleRad);
|
||||
if (angleDeg < 0) {
|
||||
angleDeg += 360;
|
||||
}
|
||||
|
||||
return angleDeg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据风向角度确定16方向索引
|
||||
*/
|
||||
private static int getDirectionIndex(double direction) {
|
||||
// 将角度调整到0-360范围内
|
||||
double normalizedDir = direction % 360;
|
||||
if (normalizedDir < 0) normalizedDir += 360;
|
||||
|
||||
// 计算16方向索引(每个方向22.5度,北风为0-11.25和348.75-360)
|
||||
int index = (int) Math.floor((normalizedDir + 11.25) / 22.5) % 16;
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证输入数据
|
||||
*/
|
||||
private static void validateInputData(List<Double> uList,
|
||||
List<Double> vList) {
|
||||
if (uList == null || vList == null) {
|
||||
throw new IllegalArgumentException("U、V分量数据不能为null");
|
||||
}
|
||||
|
||||
if (uList.size() != vList.size()) {
|
||||
throw new IllegalArgumentException("U、V分量数据长度不一致");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package org.jeecg.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class WindDataLineVO {
|
||||
public List<String> time;
|
||||
public List<String> temperature;
|
||||
public List<String> pressure;
|
||||
public List<String> humidity;
|
||||
public List<String> windSpeed;
|
||||
public String mean; // 平均风速
|
||||
public String max; // 最大风速
|
||||
public String min; // 最小风速
|
||||
public String stdDev; // 风速标准差
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package org.jeecg.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class WindRoseData {
|
||||
private String direction; // 方向名称
|
||||
private double angle; // 方向角度
|
||||
private double frequency; // 频率(%)
|
||||
private int count;
|
||||
|
||||
public WindRoseData(String direction, double angle) {
|
||||
this.direction = direction;
|
||||
this.angle = angle;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user