添加中央气象台地面气象预警数据源

refactor
shiyi 9 months ago
parent 4520c5c24d
commit 443a226fd7

@ -63,7 +63,7 @@ public class TableConfig {
// private final String configPath = Objects.requireNonNull(this.getClass().getClassLoader().getResource("config/tableConf.json")).getPath();
private final String configPath = System.getProperty("user.dir") +"/tableConf.json";
@PostConstruct
private void initTableConfig() {
public void initTableConfig() {
readConfig();
initLonList();
initLatList();

@ -58,7 +58,10 @@ public class HttpClientUtils {
public static String sendGet(String url, Map<String, String> params) throws IOException {
StringBuilder stringBuilder = new StringBuilder();
if (!CollectionUtils.isEmpty(params)) {
params.keySet().forEach(res -> {
for (String res : params.keySet()) {
if (res == null || params.get(res) == null) {
continue;
}
if (StringUtils.isNotBlank(stringBuilder)) {
stringBuilder.append("&");
} else {
@ -69,7 +72,7 @@ public class HttpClientUtils {
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
});
}
}
Request request = new Builder()
.url(url + stringBuilder)

@ -7,6 +7,7 @@ import com.htfp.weather.web.pojo.response.Result;
import com.htfp.weather.web.pojo.response.SurfaceWeatherWarning;
import com.htfp.weather.web.pojo.response.TimeSeriesDataset;
import com.htfp.weather.web.service.surfaceapi.CaiYunServiceImpl;
import com.htfp.weather.web.service.surfaceapi.CmaServiceImpl;
import com.htfp.weather.web.service.surfaceapi.HeFengServiceImpl;
import com.htfp.weather.web.service.IDataService;
import com.htfp.weather.web.service.surfaceapi.ISurfaceDataService;
@ -29,6 +30,8 @@ public class SurfaceWeatherController {
@Resource(name = "hefeng")
ISurfaceDataService surfaceDataService;
@Resource
CmaServiceImpl cmaService;
@PostMapping("/querySurfaceNowWeather")
public Result queryNowWeather(@Validated @RequestBody Position2D position2D) throws Exception {
double lat = position2D.getLatitude();
@ -58,7 +61,8 @@ public class SurfaceWeatherController {
double lat = position2D.getLatitude();
double lon = position2D.getLongitude();
log.info("[data-server] 地面气象预警信息查询 start: param={}", position2D);
List<SurfaceWeatherWarning> warning = surfaceDataService.getSurfaceWarning(lat, lon);
// List<SurfaceWeatherWarning> warning = surfaceDataService.getSurfaceWarning(lat, lon);
List<SurfaceWeatherWarning> warning = cmaService.getSurfaceWarning(lat, lon);
log.info("[data-server] 地面气象预警信息查询 end");
return Result.success(warning);
}

@ -21,9 +21,12 @@ public enum ErrorCode {
// 数据查询
HE_FENG_THIS_AREA_HAVE_NO_DATA(3001, "查询的数据或地区不存在"),
HE_FENG_REQUEST_ERROR(3002, "查询请求错误"),
CAI_YUN_REQUEST_ERROR(4002, "查询请求错误"),
CAI_YUN_DATA_PARSE_ERROR(4003, "数据解析错误"),
CAI_YUN_REQUEST_ERROR(3003, "查询请求错误"),
CAI_YUN_DATA_PARSE_ERROR(3004, "数据解析错误"),
CMA_REQUEST_ERROR(3005, "查询请求错误"),
CMA_DATA_PARSE_ERROR(3006, "数据解析错误"),
TIANDITU_REQUEST_ERROR(3007, "查询请求错误"),
TIANDITU_DATA_PARSE_ERROR(3008, "数据解析错误"),
IMPORT_DATA_INITIAL_FAILED(5001, "导入数据初始化失败"),
NO_NC_OR_GRIB_FILES(5002, "文件夹不存在或中没有对应的气象数据文件"),

@ -0,0 +1,32 @@
package com.htfp.weather.web.pojo.cma;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
@Data
public class AddressComponent {
private String address;
private String city;
@JsonProperty("county_code")
private String countyCode;
private String nation;
@JsonProperty("poi_position")
private String poiPosition;
private String county;
@JsonProperty("city_code")
private String cityCode;
@JsonProperty("address_position")
private String addressPosition;
private String poi;
@JsonProperty("province_code")
private String provinceCode;
private String province;
private String road;
@JsonProperty("road_distance")
private int roadDistance;
@JsonProperty("poi_distance")
private int poiDistance;
@JsonProperty("address_distance")
private int addressDistance;
}

@ -0,0 +1,22 @@
package com.htfp.weather.web.pojo.cma;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
/**
* @Author : shiyi
* @Date : 2024/7/2 16:13
* @Description :
*/
@Data
public class AntiGeoCodeResponse {
Result result;
String msg;
String status;
@Data @JsonIgnoreProperties(value = {"formattedAddress", "location"}, ignoreUnknown = true)
public static class Result {
AddressComponent addressComponent;
}
}

@ -0,0 +1,32 @@
package com.htfp.weather.web.pojo.cma;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
/**
* @Author : shiyi
* @Date : 2024/7/1 17:58
* @Description :
*/
@Data @JsonIgnoreProperties(value = {"warnDefine"}, ignoreUnknown = true)
public class CmaWarning {
private String expires;
private String procince;
private Integer leveltype;
private String signalypecode;
private Double lon;
private Double lat;
private Integer zoom;
private String issuecontent; // 发布内容
private String signallevelcode; // 颜色等级: YELLOW, BLUE, ORANGE, RED
private String reference;
private String areaId; // 县级行政编码
private String signallevel; // 预警等级:黄色,蓝色,橙色,红色
private String dataid; // 预警数据ID
private String sender; // 数据发布单位
private String codename; // 预警信号名称
private String name; // 行政单位名称
private String time; // 发布时间
private String msgtype; // 消息类型ALERT、UPDATE
private String procincecode; // 省级行政编码(前两位)
}

@ -1,5 +1,6 @@
package com.htfp.weather.web.pojo.response;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
/**
@ -10,10 +11,12 @@ import lombok.Data;
@Data
public class SurfaceWeatherWarning {
String pubTime;
// String startTime;
String endTime;
String title;
String text;
String typeName;
String severityColor;
String source;
@JsonIgnore
String dataid;
}

@ -0,0 +1,154 @@
package com.htfp.weather.web.service.surfaceapi;
import com.htfp.weather.info.Constant;
import com.htfp.weather.utils.DateTimeUtils;
import com.htfp.weather.utils.JSONUtils;
import com.htfp.weather.web.exception.AppException;
import com.htfp.weather.web.exception.ErrorCode;
import com.htfp.weather.web.pojo.cma.AntiGeoCodeResponse;
import com.htfp.weather.web.pojo.cma.CmaWarning;
import com.htfp.weather.web.pojo.response.NowWeatherStatus;
import com.htfp.weather.web.pojo.response.SurfaceWeatherWarning;
import com.htfp.weather.web.pojo.response.TimeSeriesDataset;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static com.htfp.weather.utils.HttpClientUtils.sendGet;
/**
* @Author : shiyi
* @Date : 2024/7/2 14:02
* @Description :
*/
@Slf4j
@Service("cma")
public class CmaServiceImpl implements ISurfaceDataService{
@Value("${tianditu.key}") @Setter
private String keyOfTianDiTu;
private Map<String, List<SurfaceWeatherWarning>> warningCache = new ConcurrentHashMap<>();
@Override
public NowWeatherStatus getNowSurfaceWeatherStatus(double lat, double lon) throws Exception {
return null;
}
@Override
public TimeSeriesDataset getForecastSeries(double lat, double lon) throws Exception {
return null;
}
@Override
public List<SurfaceWeatherWarning> getSurfaceWarning(double lat, double lon) throws Exception {
String countyId = getCountyIdByLatLon(lat, lon);
return getSurfaceWarningByCounty(countyId);
}
public List<SurfaceWeatherWarning> getSurfaceWarningByCounty(String countyCode) throws Exception {
return warningCache.get(countyCode);
// else {
// // 如果缓存中没有,则先查一遍
// updateCmaWarning(countyCode);
// return warningCache.get(countyCode);
// }
}
/**根据经纬度获取县级行政区代码*/
public String getCountyIdByLatLon(double lat, double lon) throws Exception {
String url = "http://api.tianditu.gov.cn/geocoder";
HashMap<String, String> params = new HashMap<>();
params.put("postStr", String.format("{'lon':%.6f,'lat':%.6f,'ver':1}", lon, lat));
params.put("type", "geocode");
params.put("tk", keyOfTianDiTu);
AntiGeoCodeResponse antiGeoCodeResponse = sendGet(url, params, AntiGeoCodeResponse.class);
if (!"ok".equals(antiGeoCodeResponse.getMsg())) {
throw new AppException(ErrorCode.TIANDITU_REQUEST_ERROR, "获取行政区划信息失败:" + antiGeoCodeResponse.getMsg());
}
String countyCode = antiGeoCodeResponse.getResult().getAddressComponent().getCountyCode();
if (StringUtils.isNotEmpty(countyCode)) {
return countyCode.substring(countyCode.length()-6);
} else {
throw new AppException(ErrorCode.TIANDITU_DATA_PARSE_ERROR, "获取行政区代码失败:" + antiGeoCodeResponse.getMsg());
}
}
/**
* 线
* */
@Scheduled(cron = "0 20,50 * * * ?")
@PostConstruct
public void updateCmaWarning() {
updateCmaWarning(null); // 更新全国预警数据
}
/**
* @param countyCode */
public void updateCmaWarning(String countyCode) {
String url = "https://data.cma.cn/dataGis/disasterWarning/getWarningDataByCnty";
// 2小时前24小时后
OffsetDateTime startTime = DateTimeUtils.offsetDateTimeToSystemZone(OffsetDateTime.now()).minusHours(2);
OffsetDateTime endTime = startTime.plusHours(24);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd+HH:mm:ss");
HashMap<String, String> params = new HashMap<>();
params.put("startTime", startTime.format(formatter));
params.put("endTime", endTime.format(formatter));
params.put("provinceCode", countyCode);
log.info("更新预警数据 start{}", params);
try {
if (params.get("provinceCode") == null) {
warningCache.clear();
}
String jsonStr = sendGet(url, params);
List<CmaWarning> cmaWarningList = JSONUtils.json2list(jsonStr, CmaWarning.class);
for (CmaWarning cmaWarning : cmaWarningList) {
String countyId = cmaWarning.getAreaId();
String dataid = cmaWarning.getDataid();
SurfaceWeatherWarning surfaceWeatherWarning = buildSurfaceWeatherWarning(cmaWarning);
if (! warningCache.containsKey(countyId)) {
warningCache.put(countyId, new ArrayList<>());
}
List<SurfaceWeatherWarning> countyCacheList = warningCache.get(countyId);
if (countyCacheList.stream().noneMatch(w -> dataid.equals(w.getDataid()))) {
// 该数据未缓存过
warningCache.get(countyId).add(surfaceWeatherWarning);
}
}
} catch (Exception e) {
log.error("[中央气象台] 预警信息请求结果处理错误, {}", e.getMessage());
}
log.info("更新预警数据end");
}
private SurfaceWeatherWarning buildSurfaceWeatherWarning(CmaWarning cmaWarning) {
SurfaceWeatherWarning surfaceWeatherWarning = new SurfaceWeatherWarning();
surfaceWeatherWarning.setSource(cmaWarning.getSender());
surfaceWeatherWarning.setSeverityColor(cmaWarning.getSignallevelcode().toUpperCase());
surfaceWeatherWarning.setText(cmaWarning.getIssuecontent());
String startTime = cmaWarning.getTime(); // NOTE 2024/7/2: 中央气象台默认使用 GMT+8 北京时
if (StringUtils.isNotEmpty(startTime)) {
surfaceWeatherWarning.setPubTime(OffsetDateTime.parse(startTime + "+08:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssxxx")).format(Constant.API_TIME_FORMATTER)
);
}
String endTime = cmaWarning.getExpires();
if (StringUtils.isNotEmpty(endTime)) {
surfaceWeatherWarning.setEndTime(OffsetDateTime.parse(endTime + "+08:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssxxx")).format(Constant.API_TIME_FORMATTER)
);
}
surfaceWeatherWarning.setTypeName(cmaWarning.getCodename());
surfaceWeatherWarning.setTitle(String.format("%s发布:%s%s预警", cmaWarning.getSender(), cmaWarning.getCodename(), cmaWarning.getSignallevel()));
surfaceWeatherWarning.setDataid(cmaWarning.getDataid());
return surfaceWeatherWarning;
}
}

@ -3,7 +3,8 @@ hefeng:
key: 4df66bb82da04b5bb85624874209fc73
caiyun:
key: Tc9tgOYr5jlPPlEw
tianditu:
key: 31094f552e1133bc7a82cfea7f03d74a
# tablestore
tablestore:
endpoint: https://gfs-test.cn-hangzhou.ots.aliyuncs.com

@ -17,6 +17,10 @@ spring:
auth: true
enable: true
required: true
task:
scheduling:
pool:
size: 3
mail:
send:
from: shiyi@htsdfp.com

@ -0,0 +1,46 @@
package com.htfp.weather.web.service.surfaceapi;
import com.htfp.weather.web.exception.AppException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import static org.junit.jupiter.api.Assertions.*;
/**
* @Author : shiyi
* @Date : 2024/7/2 16:43
* @Description :
*/
@SpringBootTest
class CmaServiceImplTest {
@Resource
CmaServiceImpl cmaService;
@Test
void normalTest() throws Exception {
String countyIdByLatLon = cmaService.getCountyIdByLatLon(39.9042, 116.4073);
Assertions.assertEquals("110101", countyIdByLatLon);
}
@Test
void outOfRange() throws Exception {
cmaService.getCountyIdByLatLon(39.9042, 166.4073);
Assertions.assertThrows(AppException.class, () -> {
cmaService.getCountyIdByLatLon(39.9042, 166.4073);
});
Assertions.assertThrows(AppException.class, () -> {
cmaService.getCountyIdByLatLon(139.9042, 166.4073);
});
}
@Test
void updateCmaWarning() throws Exception {
cmaService.updateCmaWarning();
System.out.println(cmaService.getSurfaceWarningByCounty("310000"));
}
@Test
void getWarningByLocation() throws Exception {
System.out.println(cmaService.getSurfaceWarning(41.22, 123.07));
}
}

@ -3,10 +3,12 @@ hefeng:
key: 4df66bb82da04b5bb85624874209fc73
caiyun:
key: Tc9tgOYr5jlPPlEw
tianditu:
key: 31094f552e1133bc7a82cfea7f03d74a
# tablestore
tablestore:
endpoint: https://gfs-test.cn-hangzhou.ots.aliyuncs.com
accessId: LTAI5tDphvXLfwKJEmVQqrVz
accessKey: ksioVP1S36PI6plkIIRN4A2xLB94uc
instanceName: gfs-test

Loading…
Cancel
Save