diff --git a/tablestore-grid-master/src/main/java/com/aliyun/tablestore/grid/TableStoreGridConfig.java b/tablestore-grid-master/src/main/java/com/aliyun/tablestore/grid/TableStoreGridConfig.java index 2958ddf..45c7480 100644 --- a/tablestore-grid-master/src/main/java/com/aliyun/tablestore/grid/TableStoreGridConfig.java +++ b/tablestore-grid-master/src/main/java/com/aliyun/tablestore/grid/TableStoreGridConfig.java @@ -10,7 +10,7 @@ public class TableStoreGridConfig { private String metaTableName; private String dataTableName; - private long dataSizeLimitForFetch = 20 * 1024 * 1024; + private long dataSizeLimitForFetch = 24 * 1024 * 1024; public String getTableStoreEndpoint() { return tableStoreEndpoint; diff --git a/tablestore-grid-master/src/main/java/com/aliyun/tablestore/grid/model/GridDataSetMeta.java b/tablestore-grid-master/src/main/java/com/aliyun/tablestore/grid/model/GridDataSetMeta.java index 2852b0c..3376667 100644 --- a/tablestore-grid-master/src/main/java/com/aliyun/tablestore/grid/model/GridDataSetMeta.java +++ b/tablestore-grid-master/src/main/java/com/aliyun/tablestore/grid/model/GridDataSetMeta.java @@ -1,11 +1,13 @@ package com.aliyun.tablestore.grid.model; +import lombok.ToString; import ucar.ma2.DataType; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +@ToString public class GridDataSetMeta { private String gridDataSetId; diff --git a/tablestore-grid-master/src/main/java/com/aliyun/tablestore/grid/model/grid/Grid4D.java b/tablestore-grid-master/src/main/java/com/aliyun/tablestore/grid/model/grid/Grid4D.java index 0cd97bb..b23e327 100644 --- a/tablestore-grid-master/src/main/java/com/aliyun/tablestore/grid/model/grid/Grid4D.java +++ b/tablestore-grid-master/src/main/java/com/aliyun/tablestore/grid/model/grid/Grid4D.java @@ -1,6 +1,8 @@ package com.aliyun.tablestore.grid.model.grid; +import org.checkerframework.checker.units.qual.A; import ucar.ma2.DataType; +import ucar.ma2.Index; import java.nio.ByteBuffer; import java.util.Arrays; @@ -27,4 +29,36 @@ public class Grid4D extends Grid { return new Grid3D(newBuffer, dataType, Arrays.copyOfRange(origin, 1, origin.length), Arrays.copyOfRange(shape, 1, shape.length)); } + + public float[][][][] section(int[] origin, int[] shape) { + // // calculate rank + // int rank = 0; + // for (int size : shape) { + // if (size > 1) { + // rank++; + // } + // } + // buffer.getShort() + // switch (rank) { + // case 4: { + // float[][][][] result = new float[shape[0]][shape[1]][shape[2]][shape[3]]; + // Index index = Index.factory(shape); + // index.currentElement(); + // for (int i = 0; i < shape[0]; i++) + // float[][][] grid3D = getGrid3D(i).section(Arrays.copyOfRange(origin, 1, origin.length), + // Arrays.copyOfRange(shape, 1, shape.length)); + // for (int j = 0; j < shape[1]; j++) + // break; + // } + // default: + // break; + // } + // // calculate stride + // int[] stride = new int[4]; + // stride[3] = 1; + // for (int i = 2; i >= 0; i--) { + // stride[i] = stride[i + 1] * shape[i + 1]; + // } + return null; + } } diff --git a/weather-service/gfsDataConfig.json b/weather-service/gfsDataConfig.json index b331af5..d285ab0 100644 --- a/weather-service/gfsDataConfig.json +++ b/weather-service/gfsDataConfig.json @@ -5,7 +5,7 @@ "minLat" : 0.0, "maxLat" : 55.0, "resolution" : 0.25, - "variables" : [ "DZDT", "RH", "TMP", "UGRD", "VGRD", "TCDC" ], + "variables" : [ "DZDT", "RH", "TMP", "UGRD", "VGRD", "TCDC", "PRATE" ], "pressureLevels" : [ 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 925, 950, 975, 1000 ], "heightLevels" : [ 2, 10, 20, 30, 40, 50, 80, 100 ], "saveRoot" : "./GFSData" diff --git a/weather-service/src/main/java/com/htfp/weather/download/GfsDownloader.java b/weather-service/src/main/java/com/htfp/weather/download/GfsDownloader.java index 8fc6162..1304f76 100644 --- a/weather-service/src/main/java/com/htfp/weather/download/GfsDownloader.java +++ b/weather-service/src/main/java/com/htfp/weather/download/GfsDownloader.java @@ -261,6 +261,7 @@ public class GfsDownloader extends BaseDataDownloader { for (int height : heightLevels) { stringJoiner.add(String.format("lev_%d_m_above_ground", height)); } + stringJoiner.add("lev_surface=on"); return stringJoiner.toString(); } diff --git a/weather-service/src/main/java/com/htfp/weather/griddata/common/GfsVariableHeightEnum.java b/weather-service/src/main/java/com/htfp/weather/griddata/common/GfsVariableHeightEnum.java index c913322..d769374 100644 --- a/weather-service/src/main/java/com/htfp/weather/griddata/common/GfsVariableHeightEnum.java +++ b/weather-service/src/main/java/com/htfp/weather/griddata/common/GfsVariableHeightEnum.java @@ -12,7 +12,7 @@ import java.util.concurrent.ConcurrentHashMap; public enum GfsVariableHeightEnum { // TEMP("temp", "Temperature_height_above_ground"), - RH("humidity", "Relative_humidity_cover_height_above_ground"), + RH("humidity", "Relative_humidity_height_above_ground"), WIND_SPEED("windSpeed", "Wind_speed_height_above_ground"), WIND360("wind360", "Wind_direction_height_above_ground"), // GUST("gustSpeed", "Wind_speed_gust_surface"), @@ -20,7 +20,7 @@ public enum GfsVariableHeightEnum { ; private static final ConcurrentHashMap variableName = new ConcurrentHashMap() {{ put("temp", "Temperature_height_above_ground"); - put("humidity", "Relative_humidity_cover_height_above_ground"); + put("humidity", "Relative_humidity_height_above_ground"); put("windSpeed", "Wind_speed_height_above_ground"); put("wind360", "Wind_direction_height_above_ground"); put("precip", "Precipitation_rate_surface"); diff --git a/weather-service/src/main/java/com/htfp/weather/griddata/common/GfsVariableIsobaricEnum.java b/weather-service/src/main/java/com/htfp/weather/griddata/common/GfsVariableIsobaricEnum.java index ba9fc03..defb295 100644 --- a/weather-service/src/main/java/com/htfp/weather/griddata/common/GfsVariableIsobaricEnum.java +++ b/weather-service/src/main/java/com/htfp/weather/griddata/common/GfsVariableIsobaricEnum.java @@ -12,6 +12,7 @@ import java.util.concurrent.ConcurrentHashMap; public enum GfsVariableIsobaricEnum { // TEMP("temp", "Temperature_isobaric"), + RH("humidity", "Relative_humidity_isobaric"), CLOUD("cloud", "Total_cloud_cover_isobaric"), WIND_SPEED("windSpeed", "Wind_speed_isobaric"), WIND360("wind360", "Wind_direction_isobaric"), @@ -21,6 +22,7 @@ public enum GfsVariableIsobaricEnum { put("cloud", "Total_cloud_cover_isobaric"); put("windSpeed", "Wind_speed_isobaric"); put("wind360", "Wind_direction_isobaric"); + put("humidity", "Relative_humidity_isobaric"); }}; String nameInApi; String nameInFile; diff --git a/weather-service/src/main/java/com/htfp/weather/griddata/common/TableConfig.java b/weather-service/src/main/java/com/htfp/weather/griddata/common/TableConfig.java index b6ada8e..4f9e376 100644 --- a/weather-service/src/main/java/com/htfp/weather/griddata/common/TableConfig.java +++ b/weather-service/src/main/java/com/htfp/weather/griddata/common/TableConfig.java @@ -137,7 +137,6 @@ public class TableConfig { private void initLevList() { pressureList = dataConfig.getPressureLevels(); heightList = dataConfig.getHeightLevels(); - - levSize = pressureList.length + heightList.length; + // this.levSize = Math.max(pressureList.length, heightList.length); } } diff --git a/weather-service/src/main/java/com/htfp/weather/griddata/operation/GfsDataFetcher.java b/weather-service/src/main/java/com/htfp/weather/griddata/operation/GfsDataFetcher.java index 7417d87..c9a30b1 100644 --- a/weather-service/src/main/java/com/htfp/weather/griddata/operation/GfsDataFetcher.java +++ b/weather-service/src/main/java/com/htfp/weather/griddata/operation/GfsDataFetcher.java @@ -1,17 +1,34 @@ package com.htfp.weather.griddata.operation; import com.aliyun.tablestore.grid.GridDataFetcher; +import com.aliyun.tablestore.grid.consts.AttributionEnum; import com.aliyun.tablestore.grid.model.GridDataSet; +import com.aliyun.tablestore.grid.model.GridDataSetMeta; +import com.aliyun.tablestore.grid.model.grid.Range; import com.htfp.weather.griddata.common.TableConfig; +import com.htfp.weather.griddata.utils.ValueRange; +import com.htfp.weather.info.GfsLevelsEnum; +import com.htfp.weather.utils.NdArrayUtils; +import com.htfp.weather.web.exception.AppException; +import com.htfp.weather.web.exception.ErrorCode; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.DependsOn; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; import ucar.ma2.Array; +import javax.annotation.PostConstruct; import javax.annotation.Resource; +import java.time.Duration; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; /** * @Author : shiyi @@ -23,6 +40,31 @@ import java.util.List; public class GfsDataFetcher extends BaseTableOperation { @Resource TableConfig tableConfig; + @Resource + QueryMeta queryMeta; + GridDataSetMeta lastGridDataSetMeta; + + public GridDataSetMeta getLastGridDataSetMeta() { + if (lastGridDataSetMeta == null) { + updateLastGridDataSetMeta(); + throw new AppException(ErrorCode.DATA_SET_EMPTY, ": e.getMessage()"); + } + return lastGridDataSetMeta; + } + + /** + * 每小时更新一次元数据 + */ + @Scheduled(cron = "0 30 * * * ?") @PostConstruct + public GridDataSetMeta updateLastGridDataSetMeta() { + try { + lastGridDataSetMeta = queryMeta.getLastGridDataSetMeta(); + } catch (Exception e) { + lastGridDataSetMeta = null; + } + log.info("更新最新的数据元信息: {}", lastGridDataSetMeta); + return lastGridDataSetMeta; + } /** * 获取指定变量的四维数据(time, lev, lat, lon) @@ -97,12 +139,19 @@ public class GfsDataFetcher extends BaseTableOperation { * @return {@link Array} * @throws Exception */ - public Array getProfile(String dataSetId, String variable, int iTime, int iLat, int iLon) throws Exception { - int[] origin = new int[] {iTime, 0, iLat, iLon}; - int[] shape = new int[] {1, tableConfig.levSize, 1, 1}; - GridDataSet gridDataSet = queryByTableStore(dataSetId, Collections.singletonList(variable), - origin, shape); - Array array = gridDataSet.getVariable(variable).toArray(); + public Array getProfileByVariableAndPressure(String variableName, OffsetDateTime targetTime, double latitude, double longitude) throws Exception { + lastGridDataSetMeta = getLastGridDataSetMeta(); + GridDataFetcher fetcher = tableStoreGrid.getDataFetcher(lastGridDataSetMeta); + int iTime = getTargetTimeIndex(targetTime); + int iLat= getLatitudeIndex(latitude); + int iLon = getLongitudeIndex(longitude); + int[] origin = new int[]{iTime, 0, iLat, iLon}; + int[] shape = new int[]{1,tableConfig.levSize, 1, 1}; + // TODO 2024/6/17: + fetcher.setVariablesToGet(Collections.singletonList(variableName)); + fetcher.setOriginShape(origin, shape); + GridDataSet gridDataSet = fetcher.fetch(); + Array array = gridDataSet.getVariable(variableName).toArray(); return array; } @@ -141,4 +190,107 @@ public class GfsDataFetcher extends BaseTableOperation { int[] shape = new int[] {iTImes.length, 1, 1, 1}; return queryByTableStore(dataSetId, variableList, origin, shape); } + + /** + * 查询指定范围的4维数组数据集 + * @param variableNames + * @param timeRange + * @param levRange + * @param latRange + * @param lonRange + * @return + * @throws Exception + */ + public GridDataSet queryDataset4D(List variableNames, ValueRange timeRange, ValueRange levRange, ValueRange latRange, ValueRange lonRange) throws Exception { + lastGridDataSetMeta = getLastGridDataSetMeta(); + GridDataFetcher fetcher = tableStoreGrid.getDataFetcher(lastGridDataSetMeta); + Range iTimeRange = new Range(getTargetTimeIndex(timeRange.getStart()),getTargetTimeIndex(timeRange.getEnd()) + 1); + Range iLevRage = new Range(getLevelIndex(levRange.getStart()), getLevelIndex(levRange.getEnd()) + 1); + Range iLatRange = new Range(getLatitudeIndex(latRange.getStart()),getLatitudeIndex(latRange.getEnd()) + 1); + Range iLonRange = new Range(getLongitudeIndex(lonRange.getStart()),getLongitudeIndex(lonRange.getEnd()) + 1); + int[] origin = new int[]{iTimeRange.getStart(), iLevRage.getStart(), iLatRange.getStart(), iLonRange.getStart()}; + int[] shape = new int[]{iTimeRange.getSize(), iLevRage.getSize(), iLatRange.getSize(), iLonRange.getSize()}; + // TODO 2024/6/17: + fetcher.setVariablesToGet(variableNames); + fetcher.setOriginShape(origin, shape); + GridDataSet gridDataSet = fetcher.fetch(); + return gridDataSet; + } + + + private int getTargetTimeIndex(OffsetDateTime targetTime) { + lastGridDataSetMeta = getLastGridDataSetMeta(); + 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 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; + } + + public 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; + } + + public 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 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; + } + + public int getLevelIndex(int level) { + if (GfsLevelsEnum.getByCode(level) == GfsLevelsEnum.SURFACE) { + return 0; // NOTE 2024/5/20: 0 表示地面,风速对应10m高度,其他变量对应2m高度 + } else { + return getPressureIndex(level); + } + } + + private int getForecastHourFromTargetTime(OffsetDateTime targetTime, OffsetDateTime refTime) { + return (int) Duration.between(refTime, targetTime).toHours(); + } + } diff --git a/weather-service/src/main/java/com/htfp/weather/griddata/operation/GfsDataImport.java b/weather-service/src/main/java/com/htfp/weather/griddata/operation/GfsDataImport.java index 7186ba7..4ecfd8a 100644 --- a/weather-service/src/main/java/com/htfp/weather/griddata/operation/GfsDataImport.java +++ b/weather-service/src/main/java/com/htfp/weather/griddata/operation/GfsDataImport.java @@ -42,7 +42,8 @@ import java.util.concurrent.Executors; public class GfsDataImport extends BaseTableOperation { @Resource TableConfig tableConfig; - + @Resource + GfsDataFetcher gfsDataFetcher; @Getter boolean importing = false; private final ExecutorService executorService = Executors.newFixedThreadPool(5); @@ -97,12 +98,12 @@ public class GfsDataImport extends BaseTableOperation { public List importData(OffsetDateTime refTime) throws Exception { - log.info("[tablestore] 数据导入开始, refTime = {}...", refTime); long start = System.currentTimeMillis(); List fileList = getFiles(refTime); if (CollectionUtils.isEmpty(fileList)) { throw new AppException(ErrorCode.NO_NC_OR_GRIB_FILES); } + log.info("[tablestore] 数据导入开始, refTime = {}...", refTime); String dataSetId = refTime.format(DateTimeFormatter.ofPattern(Constant.DATA_SET_ID_FORMAT_STRING)); List fileVariables = getFileVariables(tableConfig.variableList); int[] shape = new int[]{tableConfig.timeSizeMax, tableConfig.levSize, tableConfig.latSize, tableConfig.lonSize}; @@ -140,6 +141,8 @@ public class GfsDataImport extends BaseTableOperation { meta.setForecastHours(forecastHours); meta.addAttribute("reference_time", refTime.toInstant().toEpochMilli()); putMeta(meta); + gfsDataFetcher.lastGridDataSetMeta = meta; + log.info("[tablestore]: 更新最新的数据元信息: {}", meta); return finishedList; } diff --git a/weather-service/src/main/java/com/htfp/weather/griddata/utils/ValueRange.java b/weather-service/src/main/java/com/htfp/weather/griddata/utils/ValueRange.java new file mode 100644 index 0000000..aa8ae90 --- /dev/null +++ b/weather-service/src/main/java/com/htfp/weather/griddata/utils/ValueRange.java @@ -0,0 +1,35 @@ +package com.htfp.weather.griddata.utils; + +import lombok.Getter; + +import java.util.Collections; +import java.util.List; + +/** + * @Author : shiyi + * @Date : 2024/6/17 17:28 + * @Description : 经纬高范围 + */ +@Getter +public class ValueRange>{ + T start; + T end; // include + + + public ValueRange(T start, T end) { + this.start = start; + this.end = end; + } + + public ValueRange(List list) { + this.start = Collections.min(list); + this.end = Collections.max(list); + } + + @Override + public String toString() { + return "[" + start + + "," + end + + ']'; + } +} diff --git a/weather-service/src/main/java/com/htfp/weather/info/Constant.java b/weather-service/src/main/java/com/htfp/weather/info/Constant.java index 3941242..8913290 100644 --- a/weather-service/src/main/java/com/htfp/weather/info/Constant.java +++ b/weather-service/src/main/java/com/htfp/weather/info/Constant.java @@ -1,5 +1,7 @@ package com.htfp.weather.info; +import java.time.format.DateTimeFormatter; + /** * @Author : shiyi * @Date : 2024/5/8 12:02 @@ -10,5 +12,6 @@ public class Constant { public static final String DATA_FOLDER_STRING = "'UTC-'yyyyMMdd.HH"; public static final String UTC_TIME_STRING = "'UTC-'yyyyMMdd.HH"; public static final String BJT_TIME_STRING = "'BJT-'yyyyMMdd.HH"; - public static final String API_TIME_STRING = "yyyy-MM-dd'T'HH:mmxxx"; + public static final String API_TIME_STRING = "yyyy-MM-dd'T'HH:mm:ssxxx"; + public static final DateTimeFormatter API_TIME_FORMATTER = DateTimeFormatter.ISO_OFFSET_DATE_TIME; } diff --git a/weather-service/src/main/java/com/htfp/weather/info/GfsDownloadVariableEnum.java b/weather-service/src/main/java/com/htfp/weather/info/GfsDownloadVariableEnum.java index bc24dd5..954d5a2 100644 --- a/weather-service/src/main/java/com/htfp/weather/info/GfsDownloadVariableEnum.java +++ b/weather-service/src/main/java/com/htfp/weather/info/GfsDownloadVariableEnum.java @@ -13,7 +13,7 @@ public enum GfsDownloadVariableEnum { CLOUD("TCDC", "云量"), RELATIVE_HUMIDITY("RH", "相对湿度"), VERTICAL_VELOCITY("DZDT", "垂直速度"), - + PRECIPITATION("PRATE", "降水率(mm)"), ; private final String code; private final String info; diff --git a/weather-service/src/main/java/com/htfp/weather/schedule/GridDataProcessor.java b/weather-service/src/main/java/com/htfp/weather/schedule/GridDataProcessor.java index 7494d52..e05e5f2 100644 --- a/weather-service/src/main/java/com/htfp/weather/schedule/GridDataProcessor.java +++ b/weather-service/src/main/java/com/htfp/weather/schedule/GridDataProcessor.java @@ -1,8 +1,10 @@ package com.htfp.weather.schedule; +import com.aliyun.tablestore.grid.model.GridDataSetMeta; import com.htfp.weather.download.FileInfo; import com.htfp.weather.download.GfsDataConfig; import com.htfp.weather.download.GfsDownloader; +import com.htfp.weather.griddata.operation.GfsDataFetcher; import com.htfp.weather.griddata.operation.GfsDataImport; import com.htfp.weather.griddata.operation.GfsDataImport.ImportResult; import com.htfp.weather.info.Constant; @@ -41,6 +43,8 @@ public class GridDataProcessor { @Resource GfsDownloader gfsDownloader; @Resource + GfsDataFetcher gfsDataFetcher; + @Resource GfsDataConfig gfsDataConfig; @Resource ErrorReportService errorReportService; @@ -115,17 +119,19 @@ public class GridDataProcessor { return; } OffsetDateTime now = OffsetDateTime.now(); - for (File subDir : dataDir.listFiles()) { + File[] dirs = dataDir.listFiles(); + for (File subDir : dirs) { OffsetDateTime refTime = LocalDateTime.parse(subDir.getName(), DateTimeFormatter.ofPattern(Constant.DATA_FOLDER_STRING)).atOffset(ZoneOffset.UTC); if (refTime.isBefore(now.minusDays(3))) { try { FileUtils.deleteDirectory(subDir); - log.info("[schedule-end] 删除过期数据文件夹成功: {}", subDir.getName()); + log.info("[schedule] 删除过期数据文件夹成功: {}", subDir.getName()); } catch (IOException e) { - log.info("[schedule-end] 删除过期数据文件夹失败: {}", subDir.getName()); + log.info("[schedule] 删除过期数据文件夹失败: {}", subDir.getName()); } } } + log.info("[schedule-end] 过期数据清理结束"); } private void downloadFailedReport(String refTime, List finishedList, List failedList) { diff --git a/weather-service/src/main/java/com/htfp/weather/web/controller/ConfigController.java b/weather-service/src/main/java/com/htfp/weather/web/controller/ConfigController.java index 029ce84..6941629 100644 --- a/weather-service/src/main/java/com/htfp/weather/web/controller/ConfigController.java +++ b/weather-service/src/main/java/com/htfp/weather/web/controller/ConfigController.java @@ -67,7 +67,7 @@ public class ConfigController { /** * 更新tablestore数据库配置 */ - @RequestMapping("/updateDatabaseConfig") + @PostMapping("/updateDatabaseConfig") public Result updateDatabaseConfig(@RequestBody TableConfigUpdate request) { String secret = request.getSecret(); TableConfig updateConfig = request.getConfig(); @@ -95,7 +95,7 @@ public class ConfigController { /** * 更新GFS下载配置 */ - @RequestMapping("/updateDataSourceConfig") + @PostMapping("/updateDataSourceConfig") public Result updateDataSourceConfig(@Validated @RequestBody GfsConfigUpdate request) { String secret = request.getSecret(); GfsDataConfig updateConfig = request.getConfig(); @@ -115,7 +115,7 @@ public class ConfigController { /** * 下载当前时刻未来24小时的数据文件 */ - @RequestMapping("/downloadAllFiles") + @PostMapping("/downloadAllFiles") public Result downloadAllFiles(@RequestBody Map params) { String secret = params.get("secret"); validSecret(secret); @@ -142,11 +142,11 @@ public class ConfigController { /** * 下载指定时刻文件 */ - @RequestMapping("/downloadSingleFile") + @PostMapping("/downloadSingleFile") public Result downloadSingleFile(@RequestBody Map params) { String secret = params.get("secret"); validSecret(secret); - OffsetDateTime offsetDateTime = OffsetDateTime.parse(params.get("time"), DateTimeFormatter.ofPattern(Constant.API_TIME_STRING)); + OffsetDateTime offsetDateTime = OffsetDateTime.parse(params.get("time"), Constant.API_TIME_FORMATTER); String targetUtcStr = DateTimeUtils.getUTCDateTime(offsetDateTime).format(DateTimeFormatter.ofPattern(Constant.UTC_TIME_STRING)); try { gfsDownloader.iniTimeSetting(); @@ -170,11 +170,11 @@ public class ConfigController { /** * 根据起报时间将数据导入数据库 */ - @RequestMapping("/importToTablestoreByReferenceTime") + @PostMapping("/importToTablestoreByReferenceTime") public Result importData(@RequestBody Map params) { String secret = params.get("secret"); validSecret(secret); - OffsetDateTime time = OffsetDateTime.parse(params.get("time"), DateTimeFormatter.ofPattern(Constant.API_TIME_STRING)); + OffsetDateTime time = OffsetDateTime.parse(params.get("time"), Constant.API_TIME_FORMATTER); try { List finishedList = gfsDataImport.importData(time); List successList = finishedList.stream().filter(result -> result.isSuccess()).collect(Collectors.toList()); @@ -194,14 +194,22 @@ public class ConfigController { } } + @PostMapping("queryAllDataDir") + public Result queryAllDataDir(@RequestBody Map params) { + String secret = params.get("secret"); + validSecret(secret); + File dataRoot = new File(gfsDataConfig.getSaveRoot()); + List subDirList = fileService.getSubDirList(dataRoot); + return Result.success(subDirList); + } /** * 查询指定起报时间文件夹中的所有文件名 */ - @RequestMapping("/queryAllFileByReferenceTime") + @PostMapping("/queryAllFileByReferenceTime") public Result queryAllFileByReferenceTime(@RequestBody Map params) { String secret = params.get("secret"); validSecret(secret); - OffsetDateTime time = OffsetDateTime.parse(params.get("time"), DateTimeFormatter.ofPattern(Constant.API_TIME_STRING)); + OffsetDateTime time = OffsetDateTime.parse(params.get("time"), Constant.API_TIME_FORMATTER); String timeStr = DateTimeUtils.getUTCDateTime(time).format(DateTimeFormatter.ofPattern(Constant.DATA_FOLDER_STRING)); File dataDir = new File(gfsDataConfig.getSaveRoot(), timeStr); @@ -213,11 +221,11 @@ public class ConfigController { /** * 删除指定起报时间对应的文件夹 */ - @RequestMapping("/deleteDirByReferenceTime") + @PostMapping("/deleteDirByReferenceTime") public Result deleteDirByReferenceTime(@RequestBody Map params) { String secret = params.get("secret"); validSecret(secret); - OffsetDateTime time = OffsetDateTime.parse(params.get("time"), DateTimeFormatter.ofPattern(Constant.API_TIME_STRING)); + OffsetDateTime time = OffsetDateTime.parse(params.get("time"), Constant.API_TIME_FORMATTER); String timeStr = DateTimeUtils.getUTCDateTime(time).format(DateTimeFormatter.ofPattern(Constant.DATA_FOLDER_STRING)); File dataDir = new File(gfsDataConfig.getSaveRoot(), timeStr); if (gfsDataImport.isImporting()) { @@ -234,7 +242,7 @@ public class ConfigController { /** * 删除指定文件 */ - @RequestMapping("/deleteTargetFile") + @PostMapping("/deleteTargetFile") public Result deleteTargetFile(@RequestBody Map params) { String secret = params.get("secret"); validSecret(secret); diff --git a/weather-service/src/main/java/com/htfp/weather/web/controller/SurfaceWeatherController.java b/weather-service/src/main/java/com/htfp/weather/web/controller/SurfaceWeatherController.java index 2f5fbf5..8f637ea 100644 --- a/weather-service/src/main/java/com/htfp/weather/web/controller/SurfaceWeatherController.java +++ b/weather-service/src/main/java/com/htfp/weather/web/controller/SurfaceWeatherController.java @@ -10,6 +10,7 @@ import com.htfp.weather.web.service.surfaceapi.CaiYunServiceImpl; import com.htfp.weather.web.service.surfaceapi.HeFengServiceImpl; import com.htfp.weather.web.service.IDataService; import com.htfp.weather.web.service.surfaceapi.ISurfaceDataService; +import lombok.extern.slf4j.Slf4j; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -22,19 +23,21 @@ import java.util.List; * @Description : 地面天气状态 */ @CrossOrigin -@RestController +@RestController @Slf4j @RequestMapping("/htfp/weather/surface/") public class SurfaceWeatherController { - @Resource(name = "tablestore-gfs") - IDataService dataService; - @Resource(name = "caiyun") + @Resource(name = "hefeng") ISurfaceDataService surfaceDataService; @PostMapping("/now") public Result queryNowWeather(@Validated @RequestBody Position2D position2D) throws Exception { double lat = position2D.getLatitude(); double lon = position2D.getLongitude(); + log.info("[data-server] 地面实时气象信息查询 start: param={}", position2D); NowWeatherStatus nowWeatherStatus = surfaceDataService.getNowSurfaceWeatherStatus(lat, lon); + nowWeatherStatus.setLatitude(lat); + nowWeatherStatus.setLongitude(lon); + log.info("[data-server] 地面实时气象信息查询 end"); return Result.success(nowWeatherStatus); } @@ -42,7 +45,11 @@ public class SurfaceWeatherController { public Result querySurfaceForecastWeather(@Validated @RequestBody Position2D position2D) throws Exception { double lat = position2D.getLatitude(); double lon = position2D.getLongitude(); + log.info("[data-server] 地面24小时预报结果查询 start: param={}", position2D); TimeSeriesDataset forecastSeries = surfaceDataService.getForecastSeries(lat, lon); + forecastSeries.setLatitude(lat); + forecastSeries.setLongitude(lon); + log.info("[data-server] 地面24小时预报结果查询"); return Result.success(forecastSeries); } @@ -50,7 +57,9 @@ public class SurfaceWeatherController { public Result warning(@Validated @RequestBody Position2D position2D) throws Exception { double lat = position2D.getLatitude(); double lon = position2D.getLongitude(); + log.info("[data-server] 地面气象预警信息查询 start: param={}", position2D); List warning = surfaceDataService.getSurfaceWarning(lat, lon); + log.info("[data-server] 地面气象预警信息查询 end"); return Result.success(warning); } diff --git a/weather-service/src/main/java/com/htfp/weather/web/controller/UpperWeatherController.java b/weather-service/src/main/java/com/htfp/weather/web/controller/UpperWeatherController.java index 31654a8..a951c15 100644 --- a/weather-service/src/main/java/com/htfp/weather/web/controller/UpperWeatherController.java +++ b/weather-service/src/main/java/com/htfp/weather/web/controller/UpperWeatherController.java @@ -1,11 +1,13 @@ package com.htfp.weather.web.controller; +import com.htfp.weather.info.Constant; import com.htfp.weather.utils.DateTimeUtils; import com.htfp.weather.web.exception.AppException; import com.htfp.weather.web.exception.ErrorCode; import com.htfp.weather.web.pojo.request.*; import com.htfp.weather.web.pojo.response.*; import com.htfp.weather.web.service.GfsDataServiceImpl; +import lombok.extern.slf4j.Slf4j; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -20,55 +22,21 @@ import java.util.List; * @Description : */ @CrossOrigin -@RestController +@RestController @Slf4j @RequestMapping("/htfp/weather/upper/") public class UpperWeatherController { @Resource(name = "tablestore-gfs") GfsDataServiceImpl gfsDataService; - @RequestMapping("/profileByPressure") - public Result queryProfile(@Validated @RequestBody ProfileRequest profileRequest) { - OffsetDateTime time = OffsetDateTime.parse(profileRequest.getTime()); - - OffsetDateTime utcDateTime = DateTimeUtils.getUTCDateTime(time); - String variableName = profileRequest.getVariableName(); - double latitude = profileRequest.getLatitude(); - double longitude = profileRequest.getLongitude(); - ProfileResponse profileResponse = gfsDataService.getProfile(utcDateTime, variableName, latitude, longitude); - return Result.success(profileResponse); - } - - @PostMapping("/queryForecastTimeSeries") - public Result queryForecastTimeSeries(@Validated @RequestBody Position3D position3D) { - double latitude = position3D.getLatitude(); - double longitude = position3D.getLongitude(); - int level = position3D.getLevel(); - TimeSeriesDataset forecastSeries = gfsDataService.getForecastSeries(latitude, longitude, level); - return Result.success(forecastSeries); - } - - @PostMapping("/plane") - public Result queryPlane(@Validated @RequestBody PlaneRequest planeRequest) { - planeRequest.valid(); - String variableName = planeRequest.getVariableName(); - OffsetDateTime time = OffsetDateTime.parse(planeRequest.getTime()); - OffsetDateTime utcDateTime = DateTimeUtils.getUTCDateTime(time); - int level = planeRequest.getLevel(); - double minLat = planeRequest.getMinLat(); - double maxLat = planeRequest.getMaxLat(); - double minLon = planeRequest.getMinLon(); - double maxLon = planeRequest.getMaxLon(); - PlaneResponse forecastSeries = gfsDataService.getPlane(utcDateTime, variableName, level, minLat, maxLat, minLon, maxLon); - return Result.success(forecastSeries); - } - @PostMapping("/queryUpperNowWeather") public Result queryUpperNowWeather(@Validated @RequestBody Position3D position3D) { double latitude = position3D.getLatitude(); double longitude = position3D.getLongitude(); int level = position3D.getLevel(); - NowWeatherStatus nowWeather = gfsDataService.getNowWeather(latitude, longitude, level); + log.info("[data-server] 高空实时气象信息查询 start: param={}", position3D); + NowWeatherStatus nowWeather = gfsDataService.getNowWeather(level, latitude, longitude); + log.info("[data-server] 高空实时气象信息查询 end"); return Result.success(nowWeather); } @@ -87,10 +55,23 @@ public class UpperWeatherController { } } int level = multiPointsRequest.getLevel().intValue(); - List nowWeatherByMultiPoint = gfsDataService.getNowWeatherByMultiPoint(latitudeList, longitudeList, level); + log.info("[data-server] 高空航点实时气象信息查询 start: param={}", multiPointsRequest); + List nowWeatherByMultiPoint = gfsDataService.getNowWeatherByMultiPoint(level, latitudeList, longitudeList); + log.info("[data-server] 高空航点实时气象信息查询 end"); return Result.success(nowWeatherByMultiPoint); } + @PostMapping("/queryForecastTimeSeries24Hour") + public Result queryForecastTimeSeries(@Validated @RequestBody Position3D position3D) { + double latitude = position3D.getLatitude(); + double longitude = position3D.getLongitude(); + int level = position3D.getLevel(); + log.info("[data-server] 高空24小时预报结果查询 start: param={}", position3D); + TimeSeriesDataset forecastSeries = gfsDataService.getForecastSeries(level, latitude, longitude); + log.info("[data-server] 高空24小时预报结果查询 end"); + return Result.success(forecastSeries); + } + @PostMapping("/queryForecastTimeSeriesInMultiPoints") public Result queryForecastTimeSeriesInMultiPoints(@Validated @RequestBody MultiPointsRequest multiPointsRequest) { List latitudeList = new ArrayList<>(); @@ -106,8 +87,41 @@ public class UpperWeatherController { } } int level = multiPointsRequest.getLevel().intValue(); + log.info("[data-server] 高空航点24小时预报结果查询 start: param={}", multiPointsRequest); List nowWeatherByMultiPoint = gfsDataService.getForecastSeriesByMultiPoint(latitudeList, longitudeList, level); + log.info("[data-server] 高空航点24小时预报结果查询 end"); return Result.success(nowWeatherByMultiPoint); } + @RequestMapping("/queryProfileByVariableAndPressure") + public Result queryProfileByVariableAndPressure(@Validated @RequestBody ProfileRequest profileRequest) { + OffsetDateTime time = OffsetDateTime.parse(profileRequest.getTime()); + OffsetDateTime utcDateTime = DateTimeUtils.getUTCDateTime(time); + String variableName = profileRequest.getVariableName(); + double latitude = profileRequest.getLatitude(); + double longitude = profileRequest.getLongitude(); + log.info("[data-server] 高度廓线查询 start: param={}", profileRequest); + ProfileResponse profileResponse = gfsDataService.getProfileByVariableAndPressure(variableName, utcDateTime, latitude, longitude); + log.info("[data-server] 高度廓线查询 end"); + return Result.success(profileResponse); + } + + @PostMapping("/plane") + public Result queryPlane(@Validated @RequestBody PlaneRequest planeRequest) { + planeRequest.valid(); + String variableName = planeRequest.getVariableName(); + OffsetDateTime time = OffsetDateTime.parse(planeRequest.getTime()); + OffsetDateTime utcDateTime = DateTimeUtils.getUTCDateTime(time); + int level = planeRequest.getLevel(); + double minLat = planeRequest.getMinLat(); + double maxLat = planeRequest.getMaxLat(); + double minLon = planeRequest.getMinLon(); + double maxLon = planeRequest.getMaxLon(); + log.info("[data-server] 平面网格数据查询 start: param={}", planeRequest); + PlaneResponse forecastSeries = gfsDataService.getPlane(utcDateTime, variableName, level, minLat, maxLat, minLon, maxLon); + log.info("[data-server] 平面网格数据查询 end"); + return Result.success(forecastSeries); + } + + } diff --git a/weather-service/src/main/java/com/htfp/weather/web/exception/ErrorCode.java b/weather-service/src/main/java/com/htfp/weather/web/exception/ErrorCode.java index f7c8ef4..21314de 100644 --- a/weather-service/src/main/java/com/htfp/weather/web/exception/ErrorCode.java +++ b/weather-service/src/main/java/com/htfp/weather/web/exception/ErrorCode.java @@ -34,7 +34,8 @@ public enum ErrorCode { LEVEL_INDEX_ERROR(6004, "高度坐标索引错误"), INDEX_SIZE_ERROR(6005, "坐标长度不一致"), NO_SUCH_VARIABLE(6006, "找不到变量名映射"), - RESPONSE_DATA_BUILD_ERROR(6007, "查询数据结果构建错误"), + TABLESTORE_QUERY_ERROR(6007, "数据库查询错误"), + RESPONSE_DATA_BUILD_ERROR(6008, "查询数据结果构建错误"), ; private final int code; private final String msg; diff --git a/weather-service/src/main/java/com/htfp/weather/web/pojo/request/MultiPointsRequest.java b/weather-service/src/main/java/com/htfp/weather/web/pojo/request/MultiPointsRequest.java new file mode 100644 index 0000000..02f09c1 --- /dev/null +++ b/weather-service/src/main/java/com/htfp/weather/web/pojo/request/MultiPointsRequest.java @@ -0,0 +1,18 @@ +package com.htfp.weather.web.pojo.request; + +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @Author : shiyi + * @Date : 2024/6/3 19:21 + * @Description : + */ +@Data +public class MultiPointsRequest { + List pointList; + @NotNull(message = "高度层次 (level) 不能为空") + Integer level; +} diff --git a/weather-service/src/main/java/com/htfp/weather/web/pojo/request/PlaneRequest.java b/weather-service/src/main/java/com/htfp/weather/web/pojo/request/PlaneRequest.java index 19d8285..da44e2c 100644 --- a/weather-service/src/main/java/com/htfp/weather/web/pojo/request/PlaneRequest.java +++ b/weather-service/src/main/java/com/htfp/weather/web/pojo/request/PlaneRequest.java @@ -21,7 +21,7 @@ import javax.validation.constraints.NotNull; @Data public class PlaneRequest { @NotNull(message = "时间不能为空") - @DateTimeStr(format = Constant.API_TIME_STRING, message = "时间格式错误, 应为" + "yyyy-MM-ddTHH:mmxxx") + @DateTimeStr(message = "时间格式错误") String time; @NotNull(message = "变量名不能为空") diff --git a/weather-service/src/main/java/com/htfp/weather/web/pojo/request/ProfileRequest.java b/weather-service/src/main/java/com/htfp/weather/web/pojo/request/ProfileRequest.java index d1f7d4b..c13e3cf 100644 --- a/weather-service/src/main/java/com/htfp/weather/web/pojo/request/ProfileRequest.java +++ b/weather-service/src/main/java/com/htfp/weather/web/pojo/request/ProfileRequest.java @@ -16,7 +16,7 @@ import javax.validation.constraints.NotNull; @Data public class ProfileRequest { @NotNull(message = "时间不能为空") - @DateTimeStr(format = Constant.API_TIME_STRING, message = "时间格式错误, 应为" + "yyyy-MM-ddTHH:mmxxx") + @DateTimeStr(message = "时间格式错误") String time; @NotNull(message = "变量名不能为空") diff --git a/weather-service/src/main/java/com/htfp/weather/web/pojo/response/ProfileResponse.java b/weather-service/src/main/java/com/htfp/weather/web/pojo/response/ProfileResponse.java index 844a810..230c4be 100644 --- a/weather-service/src/main/java/com/htfp/weather/web/pojo/response/ProfileResponse.java +++ b/weather-service/src/main/java/com/htfp/weather/web/pojo/response/ProfileResponse.java @@ -1,6 +1,7 @@ package com.htfp.weather.web.pojo.response; import lombok.Data; +import lombok.Getter; /** * @Author : shiyi @@ -11,6 +12,8 @@ import lombok.Data; public class ProfileResponse { int[] pressureLevels; // int[ pressureHeight; + double latitude; + double longitude; float[] values; public ProfileResponse(int[] pressureLevels, float[] values) { diff --git a/weather-service/src/main/java/com/htfp/weather/web/service/FileService.java b/weather-service/src/main/java/com/htfp/weather/web/service/FileService.java index d24a724..cd09530 100644 --- a/weather-service/src/main/java/com/htfp/weather/web/service/FileService.java +++ b/weather-service/src/main/java/com/htfp/weather/web/service/FileService.java @@ -40,6 +40,23 @@ public class FileService { } return fileInfoList; } + + public List getSubDirList(File dataRoot){ + if (!dataRoot.exists() || !dataRoot.isDirectory()) { + throw new AppException(ErrorCode.DATA_FILE_OR_DIR_NOT_EXISTS); + } + List subDirList = new ArrayList<>(); + File[] files = dataRoot.listFiles(); + if (files == null || files.length == 0) { + return null; + } + for (File file : files) { + if (file.isDirectory()) { + subDirList.add(file.getName()); + } + } + return subDirList; + } public void uploadFile(String filePath){ // 文件上传逻辑 } diff --git a/weather-service/src/main/java/com/htfp/weather/web/service/GfsDataServiceImpl.java b/weather-service/src/main/java/com/htfp/weather/web/service/GfsDataServiceImpl.java index 109cf49..f9569d5 100644 --- a/weather-service/src/main/java/com/htfp/weather/web/service/GfsDataServiceImpl.java +++ b/weather-service/src/main/java/com/htfp/weather/web/service/GfsDataServiceImpl.java @@ -8,11 +8,10 @@ import com.htfp.weather.griddata.common.GfsVariableHeightEnum; import com.htfp.weather.griddata.common.GfsVariableIsobaricEnum; import com.htfp.weather.griddata.common.TableConfig; 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.GfsLevelsEnum; 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.ErrorCode; import com.htfp.weather.web.pojo.response.NowWeatherStatus; @@ -33,7 +32,6 @@ import java.time.OffsetDateTime; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.util.*; -import java.util.stream.Collectors; /** * @Author : shiyi @@ -43,90 +41,12 @@ import java.util.stream.Collectors; @Service("tablestore-gfs") @Slf4j public class GfsDataServiceImpl implements IDataService { - private static final int SURFACE_LEVEL = 9999; @Resource GfsDataFetcher gfsDataFetcher; @Resource - QueryMeta queryMeta; - @Resource 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 params = getFutureTimeIndexList(lastGridDataSetMeta); - List iTimeList = params.get("iTimeList"); - List 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 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代表地面,其他代表指定气压高度 * @return @{@link NowWeatherStatus} */ - public NowWeatherStatus getNowWeather(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()); - } - int iTime = getTargetTimeIndex(OffsetDateTime.now(), lastGridDataSetMeta); - int iLat = getLatitudeIndex(latitude); - int iLon = getLongitudeIndex(longitude); - int iLev = getLevelIndex(level); + public NowWeatherStatus getNowWeather(int level, double latitude, double longitude) { + OffsetDateTime nowTime = OffsetDateTime.now(); + ValueRange timeRange = new ValueRange<>(Collections.singletonList(nowTime)); + ValueRange levRange = new ValueRange<>(Collections.singletonList(level)); + ValueRange latRange = new ValueRange<>(Collections.singletonList(latitude)); + ValueRange lonRange = new ValueRange<>(Collections.singletonList(longitude)); List variableNames = getVariableNamesInDatabase(level); - try { - String gridDataSetId = lastGridDataSetMeta.getGridDataSetId(); - GridDataSet gridDataSet = gfsDataFetcher.getSinglePoint(gridDataSetId, variableNames, iTime, iLev, iLat, iLon); - + GridDataSet gridDataSet = gfsDataFetcher.queryDataset4D( + variableNames,timeRange, levRange, latRange, lonRange + ); final GfsLevelsEnum levelFlag = GfsLevelsEnum.getByCode(level); // 每个变量都是0维数组 - NowWeatherStatus nowWeatherStatus = buildNowWeatherInTargetPoint(gridDataSet, 0, 0, 0, levelFlag); + NowWeatherStatus nowWeatherStatus = buildNowWeatherInTargetPoint(gridDataSet, level, latitude, longitude, levelFlag); nowWeatherStatus.setLatitude(latitude); nowWeatherStatus.setLongitude(longitude); return nowWeatherStatus; @@ -171,51 +85,58 @@ public class GfsDataServiceImpl implements IDataService { * @param level * @return */ - public List getNowWeatherByMultiPoint(List latitude, List longitude, int level) { - GridDataSetMeta lastGridDataSetMeta = null; - try { - lastGridDataSetMeta = queryMeta.getLastGridDataSetMeta(); - } catch (Exception e) { - throw new AppException(ErrorCode.DATA_SET_EMPTY, ": " + e.getMessage()); - } + public List getNowWeatherByMultiPoint(int level, List latitude, List longitude) { if (latitude.size() != longitude.size()) { throw new AppException(ErrorCode.INDEX_SIZE_ERROR, ": 经纬度数组大小不一致"); } int pointSize = latitude.size(); List variableNames = getVariableNamesInDatabase(level); - int iTime = getTargetTimeIndex(OffsetDateTime.now(), lastGridDataSetMeta); - List latIndex = latitude.stream().map(this::getLatitudeIndex).collect(Collectors.toList()); - List lonIndex = longitude.stream().map(this::getLongitudeIndex).collect(Collectors.toList()); - int iMinLat = Collections.min(latIndex).intValue(); - int iMaxLat = Collections.max(latIndex).intValue(); - 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; + OffsetDateTime nowTime = OffsetDateTime.now(); + ValueRange timeRange = new ValueRange<>(Collections.singletonList(nowTime)); + ValueRange levRange = new ValueRange<>(Collections.singletonList(level)); + ValueRange latRange = new ValueRange<>(latitude); + ValueRange lonRange = new ValueRange<>(longitude); try { - - String gridDataSetId = lastGridDataSetMeta.getGridDataSetId(); - 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); - + GridDataSet gridDataSet = gfsDataFetcher.queryDataset4D( + variableNames,timeRange, levRange, latRange, lonRange + ); List nowWeatherStatusList = new ArrayList<>(); for (int i = 0; i < pointSize; i++) { - NowWeatherStatus nowWeatherStatus = new NowWeatherStatus(); + NowWeatherStatus nowWeatherStatus; // NOTE 2024/6/11: build中传入的索引和数据库的查询索引意义不同 - final GfsLevelsEnum levelFlag = GfsLevelsEnum.getByCode(level); - nowWeatherStatus = buildNowWeatherInTargetPoint(gridDataSet, 0, latIndex.get(i), lonIndex.get(i), levelFlag); - nowWeatherStatus.setLatitude(latitude.get(i)); - nowWeatherStatus.setLongitude(longitude.get(i)); + nowWeatherStatus = buildNowWeatherInTargetPoint(gridDataSet, level, latitude.get(i), longitude.get(i), levelFlag); nowWeatherStatusList.add(nowWeatherStatus); } 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 variableNames = getVariableNamesInDatabase(level); + OffsetDateTime nowTime = OffsetDateTime.now(); + ValueRange timeRange = new ValueRange<>(Arrays.asList(nowTime, nowTime.plusHours(24))); + ValueRange levRange = new ValueRange<>(Collections.singletonList(level)); + ValueRange latRange = new ValueRange<>(Collections.singletonList(latitude)); + ValueRange 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) { throw new RuntimeException(e); } @@ -223,79 +144,71 @@ public class GfsDataServiceImpl implements IDataService { /** * 获得指定点序列上的未来24小时气象预报 - * @param latitude - * @param longitude + * @param latitudeList + * @param longitudeList * @param level * @return */ - public List getForecastSeriesByMultiPoint(List latitude, List longitude, int level) { - GridDataSetMeta lastGridDataSetMeta = null; - try { - lastGridDataSetMeta = queryMeta.getLastGridDataSetMeta(); - } catch (Exception e) { - throw new AppException(ErrorCode.DATA_SET_EMPTY, ": " + e.getMessage()); - } - if (latitude.size() != longitude.size()) { + public List getForecastSeriesByMultiPoint(List latitudeList, List longitudeList, int level) { + if (latitudeList.size() != longitudeList.size()) { throw new AppException(ErrorCode.INDEX_SIZE_ERROR, ": 经纬度数组大小不一致"); } - int pointSize = latitude.size(); + int pointSize = latitudeList.size(); List variableNames = getVariableNamesInDatabase(level); - Map params = getFutureTimeIndexList(lastGridDataSetMeta); - List iTimeList = params.get("iTimeList"); - List timeList = params.get("timeList"); - int[] iTimes = iTimeList.stream().mapToInt(Integer::valueOf).toArray(); - List latIndex = latitude.stream().map(this::getLatitudeIndex).collect(Collectors.toList()); - List lonIndex = longitude.stream().map(this::getLongitudeIndex).collect(Collectors.toList()); - int iLev = getLevelIndex(level); + OffsetDateTime nowTime = OffsetDateTime.now(); + ValueRange timeRange = new ValueRange<>(Arrays.asList(nowTime, nowTime.plusHours(24))); + ValueRange levRange = new ValueRange<>(Collections.singletonList(level)); + ValueRange latRange = new ValueRange<>(latitudeList); + ValueRange lonRange = new ValueRange<>(longitudeList); try { - String gridDataSetId = lastGridDataSetMeta.getGridDataSetId(); - - 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); - + GridDataSet gridDataSet = gfsDataFetcher.queryDataset4D( + variableNames, timeRange, levRange, latRange, lonRange + ); List timeSeriesDatasetList = new ArrayList<>(); - for (int i = 0; i < pointSize; i++) { - // NOTE 2024/6/11: build中传入的索引和数据库的查询索引意义不同 final GfsLevelsEnum levelFlag = GfsLevelsEnum.getByCode(level); - TimeSeriesDataset timeSeriesDataset = buildTimeSeriesDatasetInTargetPoint(timeList, gridDataSet, 0, latIndex.get(i), lonIndex.get(i), levelFlag); - timeSeriesDataset.setLatitude(latitude.get(i)); - timeSeriesDataset.setLongitude(longitude.get(i)); + TimeSeriesDataset timeSeriesDataset = buildTimeSeriesDatasetInTargetPoint(gridDataSet, level, latitudeList.get(i), longitudeList.get(i), levelFlag); timeSeriesDatasetList.add(timeSeriesDataset); } return timeSeriesDatasetList; - } catch (Exception e) { throw new RuntimeException(e); } } - private int[] getMarginIndexByMultiPoint(List latitude, List longitude, List level) { - int minLat = Integer.MIN_VALUE; - int maxLat = Integer.MAX_VALUE; - int minLon = Integer.MIN_VALUE; - int maxLon = Integer.MAX_VALUE; - int minLev = Integer.MIN_VALUE; - int maxLev = Integer.MAX_VALUE; - - - int iMinLat = getLatitudeIndex(minLat); - int iMaxLat = getLatitudeIndex(maxLat); - int iMinLon = getLongitudeIndex(minLon); - int iMaxLon = getLongitudeIndex(maxLon); + /** + * 获取变量随气压变化的廓线 + * + * @param targetTime 目标时间 UTC + * @param variableName 数据库中的变量名 + * @param latitude 纬度 + * @param longitude 经度 + * @return {@link ProfileResponse} + */ + public ProfileResponse getProfileByVariableAndPressure(String variableName, OffsetDateTime targetTime, double latitude, double longitude) { + String targetVariable = GfsVariableIsobaricEnum.getGfsVariableName(variableName); + if (targetVariable == null) { + throw new AppException(ErrorCode.VALIDATE_ERROR, variableName + "变量在数据库中不存在"); + } + 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()); + } - return new int[]{iMinLat, iMaxLat, iMinLon, iMaxLon}; } - /** * 获取指定变量、时间、高度的二维数组数据 * * @param targetTime 目标时间 UTC - * @param variableName 数据库中的变量名 + * @param variableName api中的变量名 * @param level 高度 * @param minLat 最小纬度 * @param maxLat 最大纬度 @@ -304,39 +217,28 @@ public class GfsDataServiceImpl implements IDataService { * @return @{@link PlaneResponse} */ public PlaneResponse getPlane(OffsetDateTime targetTime, String variableName, int level, double minLat, double maxLat, double minLon, double maxLon) { - int iLev; String targetVariable; - if (level == SURFACE_LEVEL) { - iLev = 0; // NOTE 2024/5/20: 0 表示地面,风速对应10m高度,其他变量对应2m高度 + if (GfsLevelsEnum.SURFACE.equals(GfsLevelsEnum.getByCode(level))) { targetVariable = GfsVariableHeightEnum.getGfsVariableName(variableName); } else { - iLev = getPressureIndex(level); 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 iMinLat = getLatitudeIndex(minLat); - int iMaxLat = getLatitudeIndex(maxLat); - int iMinLon = getLongitudeIndex(minLon); - int iMaxLon = getLongitudeIndex(maxLon); - int latLength = iMaxLat - iMinLat + 1; - int lonLength = iMaxLon - iMinLon + 1; + List variableNames = Collections.singletonList(targetVariable); + ValueRange timeRange = new ValueRange<>(Collections.singletonList(targetTime)); + ValueRange levRange = new ValueRange<>(Collections.singletonList(level)); + ValueRange latRange = new ValueRange<>(minLat, maxLat); + ValueRange lonRange = new ValueRange<>(minLon, maxLon); try { - String gridDataSetId = lastGridDataSetMeta.getGridDataSetId(); - Array array = gfsDataFetcher.getPlane(gridDataSetId, targetVariable, iTime, iLev, iMinLat, iMinLon, latLength, lonLength); - - return buildPlane(array, iMinLat, iMinLon, latLength, lonLength); + Array array = gfsDataFetcher.queryDataset4D( + variableNames, timeRange, levRange, latRange, lonRange + ).getVariable(targetVariable).toArray(); + return buildPlane(array, minLat, maxLat, minLon, maxLon); } catch (Exception e) { throw new RuntimeException(e); } @@ -346,7 +248,7 @@ public class GfsDataServiceImpl implements IDataService { private List getVariableNamesInDatabase(int level) { List variableNames = new ArrayList<>(); - if (level == SURFACE_LEVEL) { + if (GfsLevelsEnum.SURFACE.equals(GfsLevelsEnum.getByCode(level))) { for (String e : tableConfig.getVariableList()) { String gfsVariableName = GfsVariableHeightEnum.getGfsVariableName(e); if (gfsVariableName != null) { @@ -364,7 +266,13 @@ public class GfsDataServiceImpl implements IDataService { 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(); float[][] values = (float[][]) array.reduce().copyToNDJavaArray(); planeResponse.setLatList(Arrays.copyOfRange(tableConfig.getLatList(), iMinLat, iMinLat + latLength)); @@ -376,18 +284,23 @@ public class GfsDataServiceImpl implements IDataService { /** * 构建指定位置的预报数据 - * @param timeList-时间列表 * @param gridDataSet - * @param iLevInGrid-目标点在返回的网格中的高度索引 - * @param iLatInGrid-目标点在返回的网格中的高度索引 - * @param iLonInGrid-目标点在返回的网格中的高度索引 + * @param level + * @param latitude + * @param longitude * @param levelFlag-高度层次标记,地面或高空 * @return */ - private TimeSeriesDataset buildTimeSeriesDatasetInTargetPoint(List 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(); - result.setTime(timeList); - int tsize = timeList.size(); + result.setLatitude(latitude); + result.setLongitude(longitude); + // 获取全局索引 + int levIndexGlobal = gfsDataFetcher.getLevelIndex(level); + int latIndexGlobal = gfsDataFetcher.getLatitudeIndex(latitude); + int lonIndexGlobal = gfsDataFetcher.getLongitudeIndex(longitude); + // 利用反射构建数据集 Class aClass = result.getClass(); for (Map.Entry entry : gridDataSet.getVariables().entrySet()) { @@ -401,12 +314,17 @@ public class GfsDataServiceImpl implements IDataService { if (variableNameInApi == null) { throw new AppException(ErrorCode.NO_SUCH_VARIABLE); } - + Grid4D grid4D = entry.getValue(); + int[] origin = grid4D.getOrigin(); // grid4D的起始坐标全局索引 + int[] shape = grid4D.getShape(); + int tSize = shape[0]; try { - Grid4D grid4D = entry.getValue(); String setMethodName = "set" + variableNameInApi.substring(0, 1).toUpperCase() + variableNameInApi.substring(1); 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); } catch (ReflectiveOperationException e) { throw new AppException(ErrorCode.NO_SUCH_VARIABLE, e); @@ -414,23 +332,52 @@ public class GfsDataServiceImpl implements IDataService { e.printStackTrace(); throw new AppException(ErrorCode.RESPONSE_DATA_BUILD_ERROR, e); } + + // 设置时间 + if (result.getTime() == null) { + List timeList = getResultTimeStringList(gridDataSet, tSize, origin[0]); + result.setTime(timeList); + } } return result; } + + private List getResultTimeStringList(GridDataSet gridDataSet, int timeSize, int iTimeStart) { + GridDataSetMeta meta = gridDataSet.getMeta(); + List forecastHours = meta.getForecastHours(); + long milli = (long) meta.getAttributes().get(AttributionEnum.REFERENCE_TIME.getName()); + OffsetDateTime refTime = OffsetDateTime.ofInstant(Instant.ofEpochMilli(milli), ZoneOffset.ofHours(0)); + List 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 iLevInGrid-目标点在返回的网格中的高度索引 - * @param iLatInGrid-目标点在返回的网格中的高度索引 - * @param iLonInGrid-目标点在返回的网格中的高度索引 + * @param level + * @param latitude + * @param longitude * @param levelFlag-高度层次标记,地面或高空 * @return */ - private NowWeatherStatus buildNowWeatherInTargetPoint(GridDataSet gridDataSet, int iLevInGrid, int iLatInGrid, int iLonInGrid, GfsLevelsEnum levelFlag) { - String timeStr = OffsetDateTime.now().withMinute(0).withNano(0).format(DateTimeFormatter.ofPattern(Constant.API_TIME_STRING)); + private NowWeatherStatus buildNowWeatherInTargetPoint(GridDataSet gridDataSet, int level, double latitude, double longitude, GfsLevelsEnum levelFlag) { + String timeStr = OffsetDateTime.now().withMinute(0).withNano(0).format(Constant.API_TIME_FORMATTER); NowWeatherStatus result = new NowWeatherStatus(); 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 aClass = result.getClass(); for (Map.Entry entry : gridDataSet.getVariables().entrySet()) { @@ -445,10 +392,14 @@ public class GfsDataServiceImpl implements IDataService { throw new AppException(ErrorCode.NO_SUCH_VARIABLE); } Grid4D grid4D = entry.getValue(); Index4D index = new Index4D(grid4D.getShape()); + int[] origin = grid4D.getOrigin(); // grid4D的起始坐标全局索引 try { String setMethodName = "set" + variableNameInApi.substring(0, 1).toUpperCase() + variableNameInApi.substring(1); 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) { throw new AppException(ErrorCode.NO_SUCH_VARIABLE, e); } catch (Exception e) { @@ -459,21 +410,6 @@ public class GfsDataServiceImpl implements IDataService { 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 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 getFutureTimeIndexList(GridDataSetMeta lastGridDataSetMeta) { long milli = (long) lastGridDataSetMeta.getAttributes().get(AttributionEnum.REFERENCE_TIME.getName()); @@ -492,7 +428,7 @@ public class GfsDataServiceImpl implements IDataService { if (hour >= startHour) { iTimeList.add(i); String timeStr = DateTimeUtils.getLocalZoneDateTime(refTime.plusHours(hour)) - .format(DateTimeFormatter.ofPattern(Constant.API_TIME_STRING)); + .format(Constant.API_TIME_FORMATTER); timeList.add(timeStr); } if (iTimeList.size() >= 24) { @@ -508,64 +444,8 @@ public class GfsDataServiceImpl implements IDataService { 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 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) { return (int) Duration.between(refTime, targetTime).toHours(); } - } diff --git a/weather-service/src/main/java/com/htfp/weather/web/service/surfaceapi/HeFengServiceImpl.java b/weather-service/src/main/java/com/htfp/weather/web/service/surfaceapi/HeFengServiceImpl.java index c92c744..4ac416f 100644 --- a/weather-service/src/main/java/com/htfp/weather/web/service/surfaceapi/HeFengServiceImpl.java +++ b/weather-service/src/main/java/com/htfp/weather/web/service/surfaceapi/HeFengServiceImpl.java @@ -1,5 +1,6 @@ package com.htfp.weather.web.service.surfaceapi; +import com.htfp.weather.info.Constant; import com.htfp.weather.utils.DateTimeUtils; import com.htfp.weather.utils.MeteoUtils; import com.htfp.weather.web.exception.AppException; @@ -212,7 +213,7 @@ public class HeFengServiceImpl implements ISurfaceDataService { * @return */ private BaseHeFengData processHeFengData(BaseHeFengData data) { - String format = "yyyy-MM-dd'T'HH:mmxxx"; + String format = Constant.API_TIME_STRING; if (data instanceof HeFengForecastHour) { String fxTime = DateTimeUtils.getBJTDateTimeStringFromUTC(((HeFengForecastHour) data).getFxTime(), format); ((HeFengForecastHour) data).setFxTime(fxTime); diff --git a/weather-service/src/main/java/com/htfp/weather/web/valid/DateTimeStr.java b/weather-service/src/main/java/com/htfp/weather/web/valid/DateTimeStr.java index d419042..ace6fa1 100644 --- a/weather-service/src/main/java/com/htfp/weather/web/valid/DateTimeStr.java +++ b/weather-service/src/main/java/com/htfp/weather/web/valid/DateTimeStr.java @@ -4,6 +4,7 @@ package com.htfp.weather.web.valid; import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.*; +import java.time.format.DateTimeFormatter; /** * @Author : shiyi @@ -20,7 +21,7 @@ public @interface DateTimeStr { String message() default "{javax.validation.constraints.DateTimeStr.message}"; - String format() default "yyyy-MM-dd HH:mm:ss"; + String format() default ""; Class[] groups() default {}; diff --git a/weather-service/src/main/java/com/htfp/weather/web/valid/DateTimeValidator.java b/weather-service/src/main/java/com/htfp/weather/web/valid/DateTimeValidator.java index eace7c8..9f7cd6c 100644 --- a/weather-service/src/main/java/com/htfp/weather/web/valid/DateTimeValidator.java +++ b/weather-service/src/main/java/com/htfp/weather/web/valid/DateTimeValidator.java @@ -30,8 +30,12 @@ public class DateTimeValidator implements ConstraintValidator