|
|
@ -8,11 +8,10 @@ import com.htfp.weather.griddata.common.GfsVariableHeightEnum;
|
|
|
|
import com.htfp.weather.griddata.common.GfsVariableIsobaricEnum;
|
|
|
|
import com.htfp.weather.griddata.common.GfsVariableIsobaricEnum;
|
|
|
|
import com.htfp.weather.griddata.common.TableConfig;
|
|
|
|
import com.htfp.weather.griddata.common.TableConfig;
|
|
|
|
import com.htfp.weather.griddata.operation.GfsDataFetcher;
|
|
|
|
import com.htfp.weather.griddata.operation.GfsDataFetcher;
|
|
|
|
import com.htfp.weather.griddata.operation.QueryMeta;
|
|
|
|
import com.htfp.weather.griddata.utils.ValueRange;
|
|
|
|
import com.htfp.weather.info.Constant;
|
|
|
|
import com.htfp.weather.info.Constant;
|
|
|
|
import com.htfp.weather.info.GfsLevelsEnum;
|
|
|
|
import com.htfp.weather.info.GfsLevelsEnum;
|
|
|
|
import com.htfp.weather.utils.DateTimeUtils;
|
|
|
|
import com.htfp.weather.utils.DateTimeUtils;
|
|
|
|
import com.htfp.weather.utils.NdArrayUtils;
|
|
|
|
|
|
|
|
import com.htfp.weather.web.exception.AppException;
|
|
|
|
import com.htfp.weather.web.exception.AppException;
|
|
|
|
import com.htfp.weather.web.exception.ErrorCode;
|
|
|
|
import com.htfp.weather.web.exception.ErrorCode;
|
|
|
|
import com.htfp.weather.web.pojo.response.NowWeatherStatus;
|
|
|
|
import com.htfp.weather.web.pojo.response.NowWeatherStatus;
|
|
|
@ -33,7 +32,6 @@ import java.time.OffsetDateTime;
|
|
|
|
import java.time.ZoneOffset;
|
|
|
|
import java.time.ZoneOffset;
|
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
|
import java.util.*;
|
|
|
|
import java.util.*;
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* @Author : shiyi
|
|
|
|
* @Author : shiyi
|
|
|
@ -43,90 +41,12 @@ import java.util.stream.Collectors;
|
|
|
|
@Service("tablestore-gfs")
|
|
|
|
@Service("tablestore-gfs")
|
|
|
|
@Slf4j
|
|
|
|
@Slf4j
|
|
|
|
public class GfsDataServiceImpl implements IDataService {
|
|
|
|
public class GfsDataServiceImpl implements IDataService {
|
|
|
|
private static final int SURFACE_LEVEL = 9999;
|
|
|
|
|
|
|
|
@Resource
|
|
|
|
@Resource
|
|
|
|
GfsDataFetcher gfsDataFetcher;
|
|
|
|
GfsDataFetcher gfsDataFetcher;
|
|
|
|
@Resource
|
|
|
|
@Resource
|
|
|
|
QueryMeta queryMeta;
|
|
|
|
|
|
|
|
@Resource
|
|
|
|
|
|
|
|
TableConfig tableConfig;
|
|
|
|
TableConfig tableConfig;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 获取变量随高度的变化廓线
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @param targetTime 目标时间 UTC
|
|
|
|
|
|
|
|
* @param variableName 数据库中的变量名
|
|
|
|
|
|
|
|
* @param latitude 纬度
|
|
|
|
|
|
|
|
* @param longitude 经度
|
|
|
|
|
|
|
|
* @return {@link ProfileResponse}
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public ProfileResponse getProfile(OffsetDateTime targetTime, String variableName, double latitude, double longitude) {
|
|
|
|
|
|
|
|
String targetVariable = GfsVariableIsobaricEnum.getGfsVariableName(variableName);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (targetVariable == null) {
|
|
|
|
|
|
|
|
throw new AppException(ErrorCode.VALIDATE_ERROR, variableName + "变量在数据库中不存在");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
GridDataSetMeta lastGridDataSetMeta = null;
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
lastGridDataSetMeta = queryMeta.getLastGridDataSetMeta();
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
throw new AppException(ErrorCode.DATA_SET_EMPTY, ": e.getMessage()");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int iTime = getTargetTimeIndex(targetTime, lastGridDataSetMeta);
|
|
|
|
|
|
|
|
int iLat = getLatitudeIndex(latitude);
|
|
|
|
|
|
|
|
int iLon = getLongitudeIndex(longitude);
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
String gridDataSetId = lastGridDataSetMeta.getGridDataSetId();
|
|
|
|
|
|
|
|
int[] pressureLevels = tableConfig.getPressureList();
|
|
|
|
|
|
|
|
Array array = gfsDataFetcher.getProfile(gridDataSetId, targetVariable, iTime, iLat, iLon);
|
|
|
|
|
|
|
|
float[] values = (float[]) array.copyTo1DJavaArray();
|
|
|
|
|
|
|
|
return new ProfileResponse(pressureLevels, values);
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 获取未来24小时的气象时间序列数据
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @param latitude 纬度
|
|
|
|
|
|
|
|
* @param longitude 经度
|
|
|
|
|
|
|
|
* @param level 高度:9999代表地面,其他代表指定气压高度
|
|
|
|
|
|
|
|
* @return @{@link TimeSeriesDataset}
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public TimeSeriesDataset getForecastSeries(double latitude, double longitude, int level) {
|
|
|
|
|
|
|
|
GridDataSetMeta lastGridDataSetMeta = null;
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
lastGridDataSetMeta = queryMeta.getLastGridDataSetMeta();
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
throw new AppException(ErrorCode.DATA_SET_EMPTY, ": e.getMessage()");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Map<String, List> params = getFutureTimeIndexList(lastGridDataSetMeta);
|
|
|
|
|
|
|
|
List<Integer> iTimeList = params.get("iTimeList");
|
|
|
|
|
|
|
|
List<String> timeList = params.get("timeList");
|
|
|
|
|
|
|
|
int[] iTimes = iTimeList.stream().mapToInt(Integer::valueOf).toArray();
|
|
|
|
|
|
|
|
int iLat = getLatitudeIndex(latitude);
|
|
|
|
|
|
|
|
int iLon = getLongitudeIndex(longitude);
|
|
|
|
|
|
|
|
int iLev = getLevelIndex(level);
|
|
|
|
|
|
|
|
List<String> variableNames = getVariableNamesInDatabase(level);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
String gridDataSetId = lastGridDataSetMeta.getGridDataSetId();
|
|
|
|
|
|
|
|
GridDataSet gridDataSet = gfsDataFetcher.getSeries(gridDataSetId, variableNames, iTimes, iLev, iLat, iLon);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final GfsLevelsEnum levelFlag = GfsLevelsEnum.getByCode(level);
|
|
|
|
|
|
|
|
TimeSeriesDataset timeSeriesDataset = buildTimeSeriesDatasetInTargetPoint(timeList, gridDataSet, 0, 0, 0, levelFlag);
|
|
|
|
|
|
|
|
timeSeriesDataset.setLatitude(latitude);
|
|
|
|
|
|
|
|
timeSeriesDataset.setLongitude(longitude);
|
|
|
|
|
|
|
|
return timeSeriesDataset;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* 获取当前时刻的天气状况
|
|
|
|
* 获取当前时刻的天气状况
|
|
|
@ -136,26 +56,20 @@ public class GfsDataServiceImpl implements IDataService {
|
|
|
|
* @param level 高度:9999代表地面,其他代表指定气压高度
|
|
|
|
* @param level 高度:9999代表地面,其他代表指定气压高度
|
|
|
|
* @return @{@link NowWeatherStatus}
|
|
|
|
* @return @{@link NowWeatherStatus}
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
public NowWeatherStatus getNowWeather(double latitude, double longitude, int level) {
|
|
|
|
public NowWeatherStatus getNowWeather(int level, double latitude, double longitude) {
|
|
|
|
GridDataSetMeta lastGridDataSetMeta = null;
|
|
|
|
OffsetDateTime nowTime = OffsetDateTime.now();
|
|
|
|
try {
|
|
|
|
ValueRange<OffsetDateTime> timeRange = new ValueRange<>(Collections.singletonList(nowTime));
|
|
|
|
lastGridDataSetMeta = queryMeta.getLastGridDataSetMeta();
|
|
|
|
ValueRange<Integer> levRange = new ValueRange<>(Collections.singletonList(level));
|
|
|
|
} catch (Exception e) {
|
|
|
|
ValueRange<Double> latRange = new ValueRange<>(Collections.singletonList(latitude));
|
|
|
|
throw new AppException(ErrorCode.DATA_SET_EMPTY, ": " + e.getMessage());
|
|
|
|
ValueRange<Double> lonRange = new ValueRange<>(Collections.singletonList(longitude));
|
|
|
|
}
|
|
|
|
|
|
|
|
int iTime = getTargetTimeIndex(OffsetDateTime.now(), lastGridDataSetMeta);
|
|
|
|
|
|
|
|
int iLat = getLatitudeIndex(latitude);
|
|
|
|
|
|
|
|
int iLon = getLongitudeIndex(longitude);
|
|
|
|
|
|
|
|
int iLev = getLevelIndex(level);
|
|
|
|
|
|
|
|
List<String> variableNames = getVariableNamesInDatabase(level);
|
|
|
|
List<String> variableNames = getVariableNamesInDatabase(level);
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
String gridDataSetId = lastGridDataSetMeta.getGridDataSetId();
|
|
|
|
GridDataSet gridDataSet = gfsDataFetcher.queryDataset4D(
|
|
|
|
GridDataSet gridDataSet = gfsDataFetcher.getSinglePoint(gridDataSetId, variableNames, iTime, iLev, iLat, iLon);
|
|
|
|
variableNames,timeRange, levRange, latRange, lonRange
|
|
|
|
|
|
|
|
);
|
|
|
|
final GfsLevelsEnum levelFlag = GfsLevelsEnum.getByCode(level);
|
|
|
|
final GfsLevelsEnum levelFlag = GfsLevelsEnum.getByCode(level);
|
|
|
|
// 每个变量都是0维数组
|
|
|
|
// 每个变量都是0维数组
|
|
|
|
NowWeatherStatus nowWeatherStatus = buildNowWeatherInTargetPoint(gridDataSet, 0, 0, 0, levelFlag);
|
|
|
|
NowWeatherStatus nowWeatherStatus = buildNowWeatherInTargetPoint(gridDataSet, level, latitude, longitude, levelFlag);
|
|
|
|
nowWeatherStatus.setLatitude(latitude);
|
|
|
|
nowWeatherStatus.setLatitude(latitude);
|
|
|
|
nowWeatherStatus.setLongitude(longitude);
|
|
|
|
nowWeatherStatus.setLongitude(longitude);
|
|
|
|
return nowWeatherStatus;
|
|
|
|
return nowWeatherStatus;
|
|
|
@ -171,51 +85,58 @@ public class GfsDataServiceImpl implements IDataService {
|
|
|
|
* @param level
|
|
|
|
* @param level
|
|
|
|
* @return
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
public List<NowWeatherStatus> getNowWeatherByMultiPoint(List<Double> latitude, List<Double> longitude, int level) {
|
|
|
|
public List<NowWeatherStatus> getNowWeatherByMultiPoint(int level, List<Double> latitude, List<Double> longitude) {
|
|
|
|
GridDataSetMeta lastGridDataSetMeta = null;
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
lastGridDataSetMeta = queryMeta.getLastGridDataSetMeta();
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
throw new AppException(ErrorCode.DATA_SET_EMPTY, ": " + e.getMessage());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (latitude.size() != longitude.size()) {
|
|
|
|
if (latitude.size() != longitude.size()) {
|
|
|
|
throw new AppException(ErrorCode.INDEX_SIZE_ERROR, ": 经纬度数组大小不一致");
|
|
|
|
throw new AppException(ErrorCode.INDEX_SIZE_ERROR, ": 经纬度数组大小不一致");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int pointSize = latitude.size();
|
|
|
|
int pointSize = latitude.size();
|
|
|
|
List<String> variableNames = getVariableNamesInDatabase(level);
|
|
|
|
List<String> variableNames = getVariableNamesInDatabase(level);
|
|
|
|
int iTime = getTargetTimeIndex(OffsetDateTime.now(), lastGridDataSetMeta);
|
|
|
|
OffsetDateTime nowTime = OffsetDateTime.now();
|
|
|
|
List<Integer> latIndex = latitude.stream().map(this::getLatitudeIndex).collect(Collectors.toList());
|
|
|
|
ValueRange<OffsetDateTime> timeRange = new ValueRange<>(Collections.singletonList(nowTime));
|
|
|
|
List<Integer> lonIndex = longitude.stream().map(this::getLongitudeIndex).collect(Collectors.toList());
|
|
|
|
ValueRange<Integer> levRange = new ValueRange<>(Collections.singletonList(level));
|
|
|
|
int iMinLat = Collections.min(latIndex).intValue();
|
|
|
|
ValueRange<Double> latRange = new ValueRange<>(latitude);
|
|
|
|
int iMaxLat = Collections.max(latIndex).intValue();
|
|
|
|
ValueRange<Double> lonRange = new ValueRange<>(longitude);
|
|
|
|
int iMinLon = Collections.min(lonIndex).intValue();
|
|
|
|
|
|
|
|
int iMaxLon = Collections.max(lonIndex).intValue();
|
|
|
|
|
|
|
|
int iLev = getLevelIndex(level);
|
|
|
|
|
|
|
|
int latLength = iMaxLat - iMinLat + 1;
|
|
|
|
|
|
|
|
int lonLength = iMaxLon - iMinLon + 1;
|
|
|
|
|
|
|
|
// int levLength = iMaxLev - iMinLev + 1;
|
|
|
|
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
|
|
|
|
GridDataSet gridDataSet = gfsDataFetcher.queryDataset4D(
|
|
|
|
String gridDataSetId = lastGridDataSetMeta.getGridDataSetId();
|
|
|
|
variableNames,timeRange, levRange, latRange, lonRange
|
|
|
|
int[] origin = new int[]{iTime, iLev, 0, 0};
|
|
|
|
);
|
|
|
|
int[] shape = new int[]{1, 1, tableConfig.getLatSize(), tableConfig.getLonSize()};
|
|
|
|
|
|
|
|
GridDataSet gridDataSet = gfsDataFetcher.queryByTableStore(gridDataSetId, variableNames,
|
|
|
|
|
|
|
|
origin, shape);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
List<NowWeatherStatus> nowWeatherStatusList = new ArrayList<>();
|
|
|
|
List<NowWeatherStatus> nowWeatherStatusList = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < pointSize; i++) {
|
|
|
|
for (int i = 0; i < pointSize; i++) {
|
|
|
|
NowWeatherStatus nowWeatherStatus = new NowWeatherStatus();
|
|
|
|
NowWeatherStatus nowWeatherStatus;
|
|
|
|
// NOTE 2024/6/11: build中传入的索引和数据库的查询索引意义不同
|
|
|
|
// NOTE 2024/6/11: build中传入的索引和数据库的查询索引意义不同
|
|
|
|
|
|
|
|
|
|
|
|
final GfsLevelsEnum levelFlag = GfsLevelsEnum.getByCode(level);
|
|
|
|
final GfsLevelsEnum levelFlag = GfsLevelsEnum.getByCode(level);
|
|
|
|
nowWeatherStatus = buildNowWeatherInTargetPoint(gridDataSet, 0, latIndex.get(i), lonIndex.get(i), levelFlag);
|
|
|
|
nowWeatherStatus = buildNowWeatherInTargetPoint(gridDataSet, level, latitude.get(i), longitude.get(i), levelFlag);
|
|
|
|
nowWeatherStatus.setLatitude(latitude.get(i));
|
|
|
|
|
|
|
|
nowWeatherStatus.setLongitude(longitude.get(i));
|
|
|
|
|
|
|
|
nowWeatherStatusList.add(nowWeatherStatus);
|
|
|
|
nowWeatherStatusList.add(nowWeatherStatus);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nowWeatherStatusList;
|
|
|
|
return nowWeatherStatusList;
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 获取未来24小时的气象时间序列数据
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @param latitude 纬度
|
|
|
|
|
|
|
|
* @param longitude 经度
|
|
|
|
|
|
|
|
* @param level 高度:9999代表地面,其他代表指定气压高度
|
|
|
|
|
|
|
|
* @return @{@link TimeSeriesDataset}
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public TimeSeriesDataset getForecastSeries(int level, double latitude, double longitude) {
|
|
|
|
|
|
|
|
List<String> variableNames = getVariableNamesInDatabase(level);
|
|
|
|
|
|
|
|
OffsetDateTime nowTime = OffsetDateTime.now();
|
|
|
|
|
|
|
|
ValueRange<OffsetDateTime> timeRange = new ValueRange<>(Arrays.asList(nowTime, nowTime.plusHours(24)));
|
|
|
|
|
|
|
|
ValueRange<Integer> levRange = new ValueRange<>(Collections.singletonList(level));
|
|
|
|
|
|
|
|
ValueRange<Double> latRange = new ValueRange<>(Collections.singletonList(latitude));
|
|
|
|
|
|
|
|
ValueRange<Double> lonRange = new ValueRange<>(Collections.singletonList(longitude));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
GridDataSet gridDataSet = gfsDataFetcher.queryDataset4D(
|
|
|
|
|
|
|
|
variableNames, timeRange, levRange, latRange, lonRange
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
final GfsLevelsEnum levelFlag = GfsLevelsEnum.getByCode(level);
|
|
|
|
|
|
|
|
return buildTimeSeriesDatasetInTargetPoint(gridDataSet, level, latitude, longitude, levelFlag);
|
|
|
|
} catch (Exception e) {
|
|
|
|
} catch (Exception e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -223,79 +144,71 @@ public class GfsDataServiceImpl implements IDataService {
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* 获得指定点序列上的未来24小时气象预报
|
|
|
|
* 获得指定点序列上的未来24小时气象预报
|
|
|
|
* @param latitude
|
|
|
|
* @param latitudeList
|
|
|
|
* @param longitude
|
|
|
|
* @param longitudeList
|
|
|
|
* @param level
|
|
|
|
* @param level
|
|
|
|
* @return
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
public List<TimeSeriesDataset> getForecastSeriesByMultiPoint(List<Double> latitude, List<Double> longitude, int level) {
|
|
|
|
public List<TimeSeriesDataset> getForecastSeriesByMultiPoint(List<Double> latitudeList, List<Double> longitudeList, int level) {
|
|
|
|
GridDataSetMeta lastGridDataSetMeta = null;
|
|
|
|
if (latitudeList.size() != longitudeList.size()) {
|
|
|
|
try {
|
|
|
|
|
|
|
|
lastGridDataSetMeta = queryMeta.getLastGridDataSetMeta();
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
throw new AppException(ErrorCode.DATA_SET_EMPTY, ": " + e.getMessage());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (latitude.size() != longitude.size()) {
|
|
|
|
|
|
|
|
throw new AppException(ErrorCode.INDEX_SIZE_ERROR, ": 经纬度数组大小不一致");
|
|
|
|
throw new AppException(ErrorCode.INDEX_SIZE_ERROR, ": 经纬度数组大小不一致");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int pointSize = latitude.size();
|
|
|
|
int pointSize = latitudeList.size();
|
|
|
|
List<String> variableNames = getVariableNamesInDatabase(level);
|
|
|
|
List<String> variableNames = getVariableNamesInDatabase(level);
|
|
|
|
Map<String, List> params = getFutureTimeIndexList(lastGridDataSetMeta);
|
|
|
|
OffsetDateTime nowTime = OffsetDateTime.now();
|
|
|
|
List<Integer> iTimeList = params.get("iTimeList");
|
|
|
|
ValueRange<OffsetDateTime> timeRange = new ValueRange<>(Arrays.asList(nowTime, nowTime.plusHours(24)));
|
|
|
|
List<String> timeList = params.get("timeList");
|
|
|
|
ValueRange<Integer> levRange = new ValueRange<>(Collections.singletonList(level));
|
|
|
|
int[] iTimes = iTimeList.stream().mapToInt(Integer::valueOf).toArray();
|
|
|
|
ValueRange<Double> latRange = new ValueRange<>(latitudeList);
|
|
|
|
List<Integer> latIndex = latitude.stream().map(this::getLatitudeIndex).collect(Collectors.toList());
|
|
|
|
ValueRange<Double> lonRange = new ValueRange<>(longitudeList);
|
|
|
|
List<Integer> lonIndex = longitude.stream().map(this::getLongitudeIndex).collect(Collectors.toList());
|
|
|
|
|
|
|
|
int iLev = getLevelIndex(level);
|
|
|
|
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
String gridDataSetId = lastGridDataSetMeta.getGridDataSetId();
|
|
|
|
GridDataSet gridDataSet = gfsDataFetcher.queryDataset4D(
|
|
|
|
|
|
|
|
variableNames, timeRange, levRange, latRange, lonRange
|
|
|
|
int[] origin = new int[]{iTimes[0], iLev, 0, 0};
|
|
|
|
);
|
|
|
|
// TODO 2024/6/12: 全域查询量太大,可以考虑只在航线所在区域查询,提升查询效率
|
|
|
|
|
|
|
|
int[] shape = new int[]{iTimes.length, 1, tableConfig.getLatSize(), tableConfig.getLonSize()};
|
|
|
|
|
|
|
|
GridDataSet gridDataSet = gfsDataFetcher.queryByTableStore(gridDataSetId, variableNames,
|
|
|
|
|
|
|
|
origin, shape);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
List<TimeSeriesDataset> timeSeriesDatasetList = new ArrayList<>();
|
|
|
|
List<TimeSeriesDataset> timeSeriesDatasetList = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < pointSize; i++) {
|
|
|
|
for (int i = 0; i < pointSize; i++) {
|
|
|
|
// NOTE 2024/6/11: build中传入的索引和数据库的查询索引意义不同
|
|
|
|
|
|
|
|
final GfsLevelsEnum levelFlag = GfsLevelsEnum.getByCode(level);
|
|
|
|
final GfsLevelsEnum levelFlag = GfsLevelsEnum.getByCode(level);
|
|
|
|
TimeSeriesDataset timeSeriesDataset = buildTimeSeriesDatasetInTargetPoint(timeList, gridDataSet, 0, latIndex.get(i), lonIndex.get(i), levelFlag);
|
|
|
|
TimeSeriesDataset timeSeriesDataset = buildTimeSeriesDatasetInTargetPoint(gridDataSet, level, latitudeList.get(i), longitudeList.get(i), levelFlag);
|
|
|
|
timeSeriesDataset.setLatitude(latitude.get(i));
|
|
|
|
|
|
|
|
timeSeriesDataset.setLongitude(longitude.get(i));
|
|
|
|
|
|
|
|
timeSeriesDatasetList.add(timeSeriesDataset);
|
|
|
|
timeSeriesDatasetList.add(timeSeriesDataset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return timeSeriesDatasetList;
|
|
|
|
return timeSeriesDatasetList;
|
|
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
} catch (Exception e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private int[] getMarginIndexByMultiPoint(List<Double> latitude, List<Double> longitude, List<Double> level) {
|
|
|
|
/**
|
|
|
|
int minLat = Integer.MIN_VALUE;
|
|
|
|
* 获取变量随气压变化的廓线
|
|
|
|
int maxLat = Integer.MAX_VALUE;
|
|
|
|
*
|
|
|
|
int minLon = Integer.MIN_VALUE;
|
|
|
|
* @param targetTime 目标时间 UTC
|
|
|
|
int maxLon = Integer.MAX_VALUE;
|
|
|
|
* @param variableName 数据库中的变量名
|
|
|
|
int minLev = Integer.MIN_VALUE;
|
|
|
|
* @param latitude 纬度
|
|
|
|
int maxLev = Integer.MAX_VALUE;
|
|
|
|
* @param longitude 经度
|
|
|
|
|
|
|
|
* @return {@link ProfileResponse}
|
|
|
|
|
|
|
|
*/
|
|
|
|
int iMinLat = getLatitudeIndex(minLat);
|
|
|
|
public ProfileResponse getProfileByVariableAndPressure(String variableName, OffsetDateTime targetTime, double latitude, double longitude) {
|
|
|
|
int iMaxLat = getLatitudeIndex(maxLat);
|
|
|
|
String targetVariable = GfsVariableIsobaricEnum.getGfsVariableName(variableName);
|
|
|
|
int iMinLon = getLongitudeIndex(minLon);
|
|
|
|
if (targetVariable == null) {
|
|
|
|
int iMaxLon = getLongitudeIndex(maxLon);
|
|
|
|
throw new AppException(ErrorCode.VALIDATE_ERROR, variableName + "变量在数据库中不存在");
|
|
|
|
|
|
|
|
}
|
|
|
|
return new int[]{iMinLat, iMaxLat, iMinLon, iMaxLon};
|
|
|
|
try {
|
|
|
|
|
|
|
|
int[] pressureLevels = tableConfig.getPressureList();
|
|
|
|
|
|
|
|
Array array = gfsDataFetcher.getProfileByVariableAndPressure(targetVariable, targetTime, latitude, longitude);
|
|
|
|
|
|
|
|
float[] values = (float[]) array.copyTo1DJavaArray();
|
|
|
|
|
|
|
|
ProfileResponse profileResponse = new ProfileResponse(pressureLevels, values);
|
|
|
|
|
|
|
|
profileResponse.setLatitude(latitude);
|
|
|
|
|
|
|
|
profileResponse.setLongitude(longitude);
|
|
|
|
|
|
|
|
return profileResponse;
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
throw new AppException(ErrorCode.TABLESTORE_QUERY_ERROR, e.getMessage());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* 获取指定变量、时间、高度的二维数组数据
|
|
|
|
* 获取指定变量、时间、高度的二维数组数据
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* @param targetTime 目标时间 UTC
|
|
|
|
* @param targetTime 目标时间 UTC
|
|
|
|
* @param variableName 数据库中的变量名
|
|
|
|
* @param variableName api中的变量名
|
|
|
|
* @param level 高度
|
|
|
|
* @param level 高度
|
|
|
|
* @param minLat 最小纬度
|
|
|
|
* @param minLat 最小纬度
|
|
|
|
* @param maxLat 最大纬度
|
|
|
|
* @param maxLat 最大纬度
|
|
|
@ -304,39 +217,28 @@ public class GfsDataServiceImpl implements IDataService {
|
|
|
|
* @return @{@link PlaneResponse}
|
|
|
|
* @return @{@link PlaneResponse}
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
public PlaneResponse getPlane(OffsetDateTime targetTime, String variableName, int level, double minLat, double maxLat, double minLon, double maxLon) {
|
|
|
|
public PlaneResponse getPlane(OffsetDateTime targetTime, String variableName, int level, double minLat, double maxLat, double minLon, double maxLon) {
|
|
|
|
int iLev;
|
|
|
|
|
|
|
|
String targetVariable;
|
|
|
|
String targetVariable;
|
|
|
|
if (level == SURFACE_LEVEL) {
|
|
|
|
if (GfsLevelsEnum.SURFACE.equals(GfsLevelsEnum.getByCode(level))) {
|
|
|
|
iLev = 0; // NOTE 2024/5/20: 0 表示地面,风速对应10m高度,其他变量对应2m高度
|
|
|
|
|
|
|
|
targetVariable = GfsVariableHeightEnum.getGfsVariableName(variableName);
|
|
|
|
targetVariable = GfsVariableHeightEnum.getGfsVariableName(variableName);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
iLev = getPressureIndex(level);
|
|
|
|
|
|
|
|
targetVariable = GfsVariableIsobaricEnum.getGfsVariableName(variableName);
|
|
|
|
targetVariable = GfsVariableIsobaricEnum.getGfsVariableName(variableName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (targetVariable == null) {
|
|
|
|
if (targetVariable == null) {
|
|
|
|
throw new AppException(ErrorCode.VALIDATE_ERROR, variableName + "变量在数据库中不存在");
|
|
|
|
throw new AppException(ErrorCode.VALIDATE_ERROR, variableName + "变量在数据库中不存在");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
GridDataSetMeta lastGridDataSetMeta = null;
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
lastGridDataSetMeta = queryMeta.getLastGridDataSetMeta();
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
throw new AppException(ErrorCode.DATA_SET_EMPTY, ": e.getMessage()");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int iTime = getTargetTimeIndex(targetTime, lastGridDataSetMeta);
|
|
|
|
List<String> variableNames = Collections.singletonList(targetVariable);
|
|
|
|
int iMinLat = getLatitudeIndex(minLat);
|
|
|
|
ValueRange<OffsetDateTime> timeRange = new ValueRange<>(Collections.singletonList(targetTime));
|
|
|
|
int iMaxLat = getLatitudeIndex(maxLat);
|
|
|
|
ValueRange<Integer> levRange = new ValueRange<>(Collections.singletonList(level));
|
|
|
|
int iMinLon = getLongitudeIndex(minLon);
|
|
|
|
ValueRange<Double> latRange = new ValueRange<>(minLat, maxLat);
|
|
|
|
int iMaxLon = getLongitudeIndex(maxLon);
|
|
|
|
ValueRange<Double> lonRange = new ValueRange<>(minLon, maxLon);
|
|
|
|
int latLength = iMaxLat - iMinLat + 1;
|
|
|
|
|
|
|
|
int lonLength = iMaxLon - iMinLon + 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
String gridDataSetId = lastGridDataSetMeta.getGridDataSetId();
|
|
|
|
Array array = gfsDataFetcher.queryDataset4D(
|
|
|
|
Array array = gfsDataFetcher.getPlane(gridDataSetId, targetVariable, iTime, iLev, iMinLat, iMinLon, latLength, lonLength);
|
|
|
|
variableNames, timeRange, levRange, latRange, lonRange
|
|
|
|
|
|
|
|
).getVariable(targetVariable).toArray();
|
|
|
|
return buildPlane(array, iMinLat, iMinLon, latLength, lonLength);
|
|
|
|
return buildPlane(array, minLat, maxLat, minLon, maxLon);
|
|
|
|
} catch (Exception e) {
|
|
|
|
} catch (Exception e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -346,7 +248,7 @@ public class GfsDataServiceImpl implements IDataService {
|
|
|
|
|
|
|
|
|
|
|
|
private List<String> getVariableNamesInDatabase(int level) {
|
|
|
|
private List<String> getVariableNamesInDatabase(int level) {
|
|
|
|
List<String> variableNames = new ArrayList<>();
|
|
|
|
List<String> variableNames = new ArrayList<>();
|
|
|
|
if (level == SURFACE_LEVEL) {
|
|
|
|
if (GfsLevelsEnum.SURFACE.equals(GfsLevelsEnum.getByCode(level))) {
|
|
|
|
for (String e : tableConfig.getVariableList()) {
|
|
|
|
for (String e : tableConfig.getVariableList()) {
|
|
|
|
String gfsVariableName = GfsVariableHeightEnum.getGfsVariableName(e);
|
|
|
|
String gfsVariableName = GfsVariableHeightEnum.getGfsVariableName(e);
|
|
|
|
if (gfsVariableName != null) {
|
|
|
|
if (gfsVariableName != null) {
|
|
|
@ -364,7 +266,13 @@ public class GfsDataServiceImpl implements IDataService {
|
|
|
|
return variableNames;
|
|
|
|
return variableNames;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private PlaneResponse buildPlane(Array array, int iMinLat, int iMinLon, int latLength, int lonLength) {
|
|
|
|
private PlaneResponse buildPlane(Array array, double minLat, double maxLat, double minLon, double maxLon) {
|
|
|
|
|
|
|
|
int iMinLat = gfsDataFetcher.getLatitudeIndex(minLat);
|
|
|
|
|
|
|
|
int iMaxLat = gfsDataFetcher.getLatitudeIndex(maxLat);
|
|
|
|
|
|
|
|
int iMinLon = gfsDataFetcher.getLongitudeIndex(minLon);
|
|
|
|
|
|
|
|
int iMaxLon = gfsDataFetcher.getLongitudeIndex(maxLon);
|
|
|
|
|
|
|
|
int latLength = iMaxLat - iMinLat + 1;
|
|
|
|
|
|
|
|
int lonLength = iMaxLon - iMinLon + 1;
|
|
|
|
PlaneResponse planeResponse = new PlaneResponse();
|
|
|
|
PlaneResponse planeResponse = new PlaneResponse();
|
|
|
|
float[][] values = (float[][]) array.reduce().copyToNDJavaArray();
|
|
|
|
float[][] values = (float[][]) array.reduce().copyToNDJavaArray();
|
|
|
|
planeResponse.setLatList(Arrays.copyOfRange(tableConfig.getLatList(), iMinLat, iMinLat + latLength));
|
|
|
|
planeResponse.setLatList(Arrays.copyOfRange(tableConfig.getLatList(), iMinLat, iMinLat + latLength));
|
|
|
@ -376,18 +284,23 @@ public class GfsDataServiceImpl implements IDataService {
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* 构建指定位置的预报数据
|
|
|
|
* 构建指定位置的预报数据
|
|
|
|
* @param timeList-时间列表
|
|
|
|
|
|
|
|
* @param gridDataSet
|
|
|
|
* @param gridDataSet
|
|
|
|
* @param iLevInGrid-目标点在返回的网格中的高度索引
|
|
|
|
* @param level
|
|
|
|
* @param iLatInGrid-目标点在返回的网格中的高度索引
|
|
|
|
* @param latitude
|
|
|
|
* @param iLonInGrid-目标点在返回的网格中的高度索引
|
|
|
|
* @param longitude
|
|
|
|
* @param levelFlag-高度层次标记,地面或高空
|
|
|
|
* @param levelFlag-高度层次标记,地面或高空
|
|
|
|
* @return
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
private TimeSeriesDataset buildTimeSeriesDatasetInTargetPoint(List<String> timeList, GridDataSet gridDataSet, int iLevInGrid, int iLatInGrid, int iLonInGrid, GfsLevelsEnum levelFlag) {
|
|
|
|
|
|
|
|
|
|
|
|
private TimeSeriesDataset buildTimeSeriesDatasetInTargetPoint(GridDataSet gridDataSet, int level, double latitude, double longitude,GfsLevelsEnum levelFlag) {
|
|
|
|
TimeSeriesDataset result = new TimeSeriesDataset();
|
|
|
|
TimeSeriesDataset result = new TimeSeriesDataset();
|
|
|
|
result.setTime(timeList);
|
|
|
|
result.setLatitude(latitude);
|
|
|
|
int tsize = timeList.size();
|
|
|
|
result.setLongitude(longitude);
|
|
|
|
|
|
|
|
// 获取全局索引
|
|
|
|
|
|
|
|
int levIndexGlobal = gfsDataFetcher.getLevelIndex(level);
|
|
|
|
|
|
|
|
int latIndexGlobal = gfsDataFetcher.getLatitudeIndex(latitude);
|
|
|
|
|
|
|
|
int lonIndexGlobal = gfsDataFetcher.getLongitudeIndex(longitude);
|
|
|
|
|
|
|
|
|
|
|
|
// 利用反射构建数据集
|
|
|
|
// 利用反射构建数据集
|
|
|
|
Class<? extends TimeSeriesDataset> aClass = result.getClass();
|
|
|
|
Class<? extends TimeSeriesDataset> aClass = result.getClass();
|
|
|
|
for (Map.Entry<String, Grid4D> entry : gridDataSet.getVariables().entrySet()) {
|
|
|
|
for (Map.Entry<String, Grid4D> entry : gridDataSet.getVariables().entrySet()) {
|
|
|
@ -401,12 +314,17 @@ public class GfsDataServiceImpl implements IDataService {
|
|
|
|
if (variableNameInApi == null) {
|
|
|
|
if (variableNameInApi == null) {
|
|
|
|
throw new AppException(ErrorCode.NO_SUCH_VARIABLE);
|
|
|
|
throw new AppException(ErrorCode.NO_SUCH_VARIABLE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
Grid4D grid4D = entry.getValue();
|
|
|
|
Grid4D grid4D = entry.getValue();
|
|
|
|
|
|
|
|
int[] origin = grid4D.getOrigin(); // grid4D的起始坐标全局索引
|
|
|
|
|
|
|
|
int[] shape = grid4D.getShape();
|
|
|
|
|
|
|
|
int tSize = shape[0];
|
|
|
|
|
|
|
|
try {
|
|
|
|
String setMethodName = "set" + variableNameInApi.substring(0, 1).toUpperCase() + variableNameInApi.substring(1);
|
|
|
|
String setMethodName = "set" + variableNameInApi.substring(0, 1).toUpperCase() + variableNameInApi.substring(1);
|
|
|
|
Method method = aClass.getDeclaredMethod(setMethodName, aClass.getDeclaredField(variableNameInApi).getType());
|
|
|
|
Method method = aClass.getDeclaredMethod(setMethodName, aClass.getDeclaredField(variableNameInApi).getType());
|
|
|
|
float[] storage = (float[]) grid4D.toArray().section(new int[]{0, iLevInGrid, iLatInGrid, iLonInGrid}, new int[]{tsize, 1, 1, 1}).copyTo1DJavaArray();
|
|
|
|
// TODO 2024/6/20: 这里的toArray操作在数据量较大的时候会比较吃内存和cpu
|
|
|
|
|
|
|
|
float[] storage = (float[]) grid4D.toArray().section(
|
|
|
|
|
|
|
|
new int[]{0, levIndexGlobal-origin[1], latIndexGlobal-origin[2], lonIndexGlobal-origin[3]}, new int[]{tSize, 1, 1, 1}
|
|
|
|
|
|
|
|
).copyTo1DJavaArray();
|
|
|
|
method.invoke(result, storage);
|
|
|
|
method.invoke(result, storage);
|
|
|
|
} catch (ReflectiveOperationException e) {
|
|
|
|
} catch (ReflectiveOperationException e) {
|
|
|
|
throw new AppException(ErrorCode.NO_SUCH_VARIABLE, e);
|
|
|
|
throw new AppException(ErrorCode.NO_SUCH_VARIABLE, e);
|
|
|
@ -414,23 +332,52 @@ public class GfsDataServiceImpl implements IDataService {
|
|
|
|
e.printStackTrace();
|
|
|
|
e.printStackTrace();
|
|
|
|
throw new AppException(ErrorCode.RESPONSE_DATA_BUILD_ERROR, e);
|
|
|
|
throw new AppException(ErrorCode.RESPONSE_DATA_BUILD_ERROR, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 设置时间
|
|
|
|
|
|
|
|
if (result.getTime() == null) {
|
|
|
|
|
|
|
|
List<String> timeList = getResultTimeStringList(gridDataSet, tSize, origin[0]);
|
|
|
|
|
|
|
|
result.setTime(timeList);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private List<String> getResultTimeStringList(GridDataSet gridDataSet, int timeSize, int iTimeStart) {
|
|
|
|
|
|
|
|
GridDataSetMeta meta = gridDataSet.getMeta();
|
|
|
|
|
|
|
|
List<String> forecastHours = meta.getForecastHours();
|
|
|
|
|
|
|
|
long milli = (long) meta.getAttributes().get(AttributionEnum.REFERENCE_TIME.getName());
|
|
|
|
|
|
|
|
OffsetDateTime refTime = OffsetDateTime.ofInstant(Instant.ofEpochMilli(milli), ZoneOffset.ofHours(0));
|
|
|
|
|
|
|
|
List<String> timeList = new ArrayList<>();
|
|
|
|
|
|
|
|
for (int i = 0; i < timeSize; i++) {
|
|
|
|
|
|
|
|
int hour = Integer.parseInt(forecastHours.get(i + iTimeStart));
|
|
|
|
|
|
|
|
String timeStr = DateTimeUtils.getLocalZoneDateTime(refTime.plusHours(hour))
|
|
|
|
|
|
|
|
.format(Constant.API_TIME_FORMATTER);
|
|
|
|
|
|
|
|
timeList.add(timeStr);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return timeList;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* 构建指定位置的当前天气
|
|
|
|
* 构建指定位置的当前天气数据
|
|
|
|
* @param gridDataSet
|
|
|
|
* @param gridDataSet
|
|
|
|
* @param iLevInGrid-目标点在返回的网格中的高度索引
|
|
|
|
* @param level
|
|
|
|
* @param iLatInGrid-目标点在返回的网格中的高度索引
|
|
|
|
* @param latitude
|
|
|
|
* @param iLonInGrid-目标点在返回的网格中的高度索引
|
|
|
|
* @param longitude
|
|
|
|
* @param levelFlag-高度层次标记,地面或高空
|
|
|
|
* @param levelFlag-高度层次标记,地面或高空
|
|
|
|
* @return
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
private NowWeatherStatus buildNowWeatherInTargetPoint(GridDataSet gridDataSet, int iLevInGrid, int iLatInGrid, int iLonInGrid, GfsLevelsEnum levelFlag) {
|
|
|
|
private NowWeatherStatus buildNowWeatherInTargetPoint(GridDataSet gridDataSet, int level, double latitude, double longitude, GfsLevelsEnum levelFlag) {
|
|
|
|
String timeStr = OffsetDateTime.now().withMinute(0).withNano(0).format(DateTimeFormatter.ofPattern(Constant.API_TIME_STRING));
|
|
|
|
String timeStr = OffsetDateTime.now().withMinute(0).withNano(0).format(Constant.API_TIME_FORMATTER);
|
|
|
|
NowWeatherStatus result = new NowWeatherStatus();
|
|
|
|
NowWeatherStatus result = new NowWeatherStatus();
|
|
|
|
result.setTime(timeStr);
|
|
|
|
result.setTime(timeStr);
|
|
|
|
|
|
|
|
result.setLatitude(latitude);
|
|
|
|
|
|
|
|
result.setLongitude(longitude);
|
|
|
|
|
|
|
|
// 获取全局索引
|
|
|
|
|
|
|
|
int levIndexGlobal = gfsDataFetcher.getLevelIndex(level);
|
|
|
|
|
|
|
|
int latIndexGlobal = gfsDataFetcher.getLatitudeIndex(latitude);
|
|
|
|
|
|
|
|
int lonIndexGlobal = gfsDataFetcher.getLongitudeIndex(longitude);
|
|
|
|
|
|
|
|
|
|
|
|
// 利用反射构建数据集
|
|
|
|
// 利用反射构建数据集
|
|
|
|
Class<? extends NowWeatherStatus> aClass = result.getClass();
|
|
|
|
Class<? extends NowWeatherStatus> aClass = result.getClass();
|
|
|
|
for (Map.Entry<String, Grid4D> entry : gridDataSet.getVariables().entrySet()) {
|
|
|
|
for (Map.Entry<String, Grid4D> entry : gridDataSet.getVariables().entrySet()) {
|
|
|
@ -445,10 +392,14 @@ public class GfsDataServiceImpl implements IDataService {
|
|
|
|
throw new AppException(ErrorCode.NO_SUCH_VARIABLE);
|
|
|
|
throw new AppException(ErrorCode.NO_SUCH_VARIABLE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Grid4D grid4D = entry.getValue(); Index4D index = new Index4D(grid4D.getShape());
|
|
|
|
Grid4D grid4D = entry.getValue(); Index4D index = new Index4D(grid4D.getShape());
|
|
|
|
|
|
|
|
int[] origin = grid4D.getOrigin(); // grid4D的起始坐标全局索引
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
String setMethodName = "set" + variableNameInApi.substring(0, 1).toUpperCase() + variableNameInApi.substring(1);
|
|
|
|
String setMethodName = "set" + variableNameInApi.substring(0, 1).toUpperCase() + variableNameInApi.substring(1);
|
|
|
|
Method method = aClass.getDeclaredMethod(setMethodName, aClass.getDeclaredField(variableNameInApi).getType());
|
|
|
|
Method method = aClass.getDeclaredMethod(setMethodName, aClass.getDeclaredField(variableNameInApi).getType());
|
|
|
|
method.invoke(result, grid4D.toArray().getFloat(index.set(0, iLevInGrid, iLatInGrid, iLonInGrid)));
|
|
|
|
// TODO 2024/6/19: 避免toArray操作,减少内存复制
|
|
|
|
|
|
|
|
method.invoke(result, grid4D.toArray().getFloat(
|
|
|
|
|
|
|
|
index.set(0, levIndexGlobal-origin[1], latIndexGlobal-origin[2], lonIndexGlobal-origin[3])
|
|
|
|
|
|
|
|
));
|
|
|
|
} catch (ReflectiveOperationException e) {
|
|
|
|
} catch (ReflectiveOperationException e) {
|
|
|
|
throw new AppException(ErrorCode.NO_SUCH_VARIABLE, e);
|
|
|
|
throw new AppException(ErrorCode.NO_SUCH_VARIABLE, e);
|
|
|
|
} catch (Exception e) {
|
|
|
|
} catch (Exception e) {
|
|
|
@ -459,21 +410,6 @@ public class GfsDataServiceImpl implements IDataService {
|
|
|
|
return result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private int getTargetTimeIndex(OffsetDateTime targetTime, GridDataSetMeta lastGridDataSetMeta) {
|
|
|
|
|
|
|
|
long milli = (long) lastGridDataSetMeta.getAttributes().get(AttributionEnum.REFERENCE_TIME.getName());
|
|
|
|
|
|
|
|
OffsetDateTime refTime = OffsetDateTime.ofInstant(Instant.ofEpochMilli(milli), ZoneOffset.ofHours(0));
|
|
|
|
|
|
|
|
if (targetTime.isBefore(refTime)) {
|
|
|
|
|
|
|
|
throw new AppException(ErrorCode.QUERY_TIME_ERROR);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
List<String> forecastHours = lastGridDataSetMeta.getForecastHours();
|
|
|
|
|
|
|
|
String targetHour = String.valueOf(getForecastHourFromTargetTime(targetTime, refTime));
|
|
|
|
|
|
|
|
if (!forecastHours.contains(targetHour)) {
|
|
|
|
|
|
|
|
throw new AppException(ErrorCode.QUERY_TIME_ERROR);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int iTime = forecastHours.indexOf(targetHour);
|
|
|
|
|
|
|
|
return iTime;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Map<String, List> getFutureTimeIndexList(GridDataSetMeta lastGridDataSetMeta) {
|
|
|
|
private Map<String, List> getFutureTimeIndexList(GridDataSetMeta lastGridDataSetMeta) {
|
|
|
|
long milli = (long) lastGridDataSetMeta.getAttributes().get(AttributionEnum.REFERENCE_TIME.getName());
|
|
|
|
long milli = (long) lastGridDataSetMeta.getAttributes().get(AttributionEnum.REFERENCE_TIME.getName());
|
|
|
@ -492,7 +428,7 @@ public class GfsDataServiceImpl implements IDataService {
|
|
|
|
if (hour >= startHour) {
|
|
|
|
if (hour >= startHour) {
|
|
|
|
iTimeList.add(i);
|
|
|
|
iTimeList.add(i);
|
|
|
|
String timeStr = DateTimeUtils.getLocalZoneDateTime(refTime.plusHours(hour))
|
|
|
|
String timeStr = DateTimeUtils.getLocalZoneDateTime(refTime.plusHours(hour))
|
|
|
|
.format(DateTimeFormatter.ofPattern(Constant.API_TIME_STRING));
|
|
|
|
.format(Constant.API_TIME_FORMATTER);
|
|
|
|
timeList.add(timeStr);
|
|
|
|
timeList.add(timeStr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (iTimeList.size() >= 24) {
|
|
|
|
if (iTimeList.size() >= 24) {
|
|
|
@ -508,64 +444,8 @@ public class GfsDataServiceImpl implements IDataService {
|
|
|
|
return map;
|
|
|
|
return map;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private int getLongitudeIndex(double lon) {
|
|
|
|
|
|
|
|
double[] lonList = tableConfig.getLonList();
|
|
|
|
|
|
|
|
if (lonList.length == 0) {
|
|
|
|
|
|
|
|
throw new AppException(ErrorCode.LONGITUDE_INDEX_ERROR, "经度列表为空, 无法查找索引");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
double minLon = lonList[0];
|
|
|
|
|
|
|
|
double maxLon = lonList[lonList.length - 1];
|
|
|
|
|
|
|
|
if (minLon > lon || lon > maxLon) {
|
|
|
|
|
|
|
|
throw new AppException(ErrorCode.LONGITUDE_INDEX_ERROR, "经度不在有效范围内: " + minLon + " ~ " + maxLon);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int nearestIndex = NdArrayUtils.findNearestIndex(lonList, lon);
|
|
|
|
|
|
|
|
if (nearestIndex == -1) {
|
|
|
|
|
|
|
|
throw new AppException(ErrorCode.LONGITUDE_INDEX_ERROR, "找不到" + lon + "的经度索引");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return nearestIndex;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private int getLatitudeIndex(double lat) {
|
|
|
|
|
|
|
|
double[] latList = tableConfig.getLatList();
|
|
|
|
|
|
|
|
if (latList.length == 0) {
|
|
|
|
|
|
|
|
throw new AppException(ErrorCode.LATITUDE_INDEX_ERROR, "纬度列表为空, 无法查找索引");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
double minLat = latList[0];
|
|
|
|
|
|
|
|
double maxLat = latList[latList.length - 1];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (minLat > lat || lat > maxLat) {
|
|
|
|
|
|
|
|
throw new AppException(ErrorCode.LATITUDE_INDEX_ERROR, "纬度不在有效范围内: " + minLat + " ~ " + maxLat);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int nearestIndex = NdArrayUtils.findNearestIndex(latList, lat);
|
|
|
|
|
|
|
|
if (nearestIndex == -1) {
|
|
|
|
|
|
|
|
throw new AppException(ErrorCode.LATITUDE_INDEX_ERROR, "找不到" + lat + "的纬度索引");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return nearestIndex;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private int getPressureIndex(int pressureLevel) {
|
|
|
|
|
|
|
|
List<Integer> levList = Arrays.stream(tableConfig.getPressureList()).boxed().collect(Collectors.toList());
|
|
|
|
|
|
|
|
if (CollectionUtils.isEmpty(levList)) {
|
|
|
|
|
|
|
|
throw new AppException(ErrorCode.LEVEL_INDEX_ERROR, "气压列表为空, 无法查找索引");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int index = levList.indexOf(pressureLevel);
|
|
|
|
|
|
|
|
if (index == -1) {
|
|
|
|
|
|
|
|
throw new AppException(ErrorCode.LATITUDE_INDEX_ERROR, "找不到" + pressureLevel + "的气压高度索引");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return index;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private int getLevelIndex(int level) {
|
|
|
|
|
|
|
|
if (level == SURFACE_LEVEL) {
|
|
|
|
|
|
|
|
return 0; // NOTE 2024/5/20: 0 表示地面,风速对应10m高度,其他变量对应2m高度
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return getPressureIndex(level);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private int getForecastHourFromTargetTime(OffsetDateTime targetTime, OffsetDateTime refTime) {
|
|
|
|
private int getForecastHourFromTargetTime(OffsetDateTime targetTime, OffsetDateTime refTime) {
|
|
|
|
return (int) Duration.between(refTime, targetTime).toHours();
|
|
|
|
return (int) Duration.between(refTime, targetTime).toHours();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|