From a8dafb65db0fa9ac055164933702b0e761e5589b Mon Sep 17 00:00:00 2001
From: zhangk <zhangk@gisuni.com>
Date: Wed, 19 Jun 2024 15:50:51 +0800
Subject: [PATCH] 添加识别降雨量范围内的水库清单接口

---
 skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/service/impl/AttResBaseServiceImpl.java |  148 +++++++++++++++++++++++++++++
 skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/constant/RainfallConstant.java          |   21 ++++
 skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/vo/RainfallGridData.java                |   43 ++++++++
 skjcmanager/skjcmanager-service/skjcmanager-sm/pom.xml                                                                    |    5 +
 skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/controller/AttResBaseController.java    |   29 +++++
 skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/vo/AttResWithRainfall.java              |   14 ++
 skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/service/IAttResBaseService.java         |    4 
 7 files changed, 264 insertions(+), 0 deletions(-)

diff --git a/skjcmanager/skjcmanager-service/skjcmanager-sm/pom.xml b/skjcmanager/skjcmanager-service/skjcmanager-sm/pom.xml
index 8132456..b0e6c9d 100644
--- a/skjcmanager/skjcmanager-service/skjcmanager-sm/pom.xml
+++ b/skjcmanager/skjcmanager-service/skjcmanager-sm/pom.xml
@@ -114,6 +114,11 @@
             <artifactId>im4java</artifactId>
             <version>1.4.0</version>
         </dependency>
+        <dependency>
+            <groupId>cn.hutool</groupId>
+            <artifactId>hutool-all</artifactId>
+            <version>5.8.25</version>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/constant/RainfallConstant.java b/skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/constant/RainfallConstant.java
new file mode 100644
index 0000000..8599b95
--- /dev/null
+++ b/skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/constant/RainfallConstant.java
@@ -0,0 +1,21 @@
+package cn.gistack.sm.sjztmd.constant;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.IntStream;
+
+public class RainfallConstant {
+
+	// 气象未来3天预报服务接口
+	public static final String FORECAST_SERVICE_URL =
+		"https://sk.hubeishuiyi.cn/sltqxyb/forecast/extraForecast/getSltRainGridForecastData/";
+
+	public static final String REQUEST_JSON_BODY = "{\"dayIndex\":\"%s\"}";
+
+	public static final String X_TOKEN = "2a1ded7e-85f2-4e42-a97a-5651d745f8e1";
+
+	public static final int[] EMPTY_ATT_RES_NUM_ARRAY = IntStream.generate(() -> 0).limit(8).toArray();
+
+	public static final List<String> RAIN_FALL_RANGES = Arrays.asList("(250,+)", "(100,250]", "(50,100]", "(25,50]"
+			, "(10,25]", "(5,10]", "(0.1,5]", "[0,0.1]");
+}
diff --git a/skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/controller/AttResBaseController.java b/skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/controller/AttResBaseController.java
index d480c47..cb8305c 100644
--- a/skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/controller/AttResBaseController.java
+++ b/skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/controller/AttResBaseController.java
@@ -9,6 +9,7 @@
 import cn.gistack.sm.sjztmd.service.IAttResBaseService;
 import cn.gistack.sm.sjztmd.vo.AttAdBaseVO;
 import cn.gistack.sm.sjztmd.vo.AttResBaseVO;
+import cn.gistack.sm.sjztmd.vo.AttResWithRainfall;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiImplicitParam;
@@ -26,6 +27,7 @@
 import org.springblade.core.tool.utils.StringUtil;
 import org.springframework.web.bind.annotation.*;
 import springfox.documentation.annotations.ApiIgnore;
+import io.swagger.annotations.*;
 
 import javax.servlet.http.HttpServletResponse;
 import java.util.List;
@@ -133,4 +135,31 @@
 		return R.status(attResBaseService.updateIsShowStatus(ids,status));
 	}
 
+
+	/**
+	 * 根据预报降雨等值面图,识别出雨量范围内的水库清单
+	 * @param minRainfall 最小降雨量
+	 * @param maxRainfall 最大降雨量
+	 * @param dayIndex 预报的时段,0-2代表未来3天
+	 * @return 水库对象列表
+	 */
+	@ApiOperation(value = "根据预报降雨等值面图,识别出雨量范围内的水库清单", notes = "根据预报降雨等值面图,识别出雨量范围内的水库清单")
+	@PostMapping(value = "/getAttResByRainfall")
+	public R<List<AttResWithRainfall>> getAttResByRainfall(
+			@ApiParam(value = "最小降雨量") @RequestParam String minRainfall,
+			@ApiParam(value = "最大降雨量") @RequestParam String maxRainfall,
+			@ApiParam(value = "预报的时段,0-2代表未来3天") @RequestParam String dayIndex) {
+		return R.data(attResBaseService.getAttResByRainfall(minRainfall, maxRainfall, dayIndex));
+	}
+
+	/**
+	 * 统计不同日降水量范围下的站点个数
+	 * @param dayIndex 预报的时段,0-2代表未来3天
+	 * @return 站点个数列表
+	 */
+	@ApiOperation(value = "统计不同日降水量范围的站点个数", notes = "统计不同日降水量范围的站点个数")
+	@PostMapping(value = "/getAttResNumByRainfall")
+	public R<int[]> getAttResNumByRainfall(@ApiParam(value = "预报的时段,0-2代表未来3天") @RequestParam String dayIndex) {
+		return R.data(attResBaseService.getAttResNumByRainfall(dayIndex));
+	}
 }
diff --git a/skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/service/IAttResBaseService.java b/skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/service/IAttResBaseService.java
index bbd33d8..b2dd909 100644
--- a/skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/service/IAttResBaseService.java
+++ b/skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/service/IAttResBaseService.java
@@ -75,4 +75,8 @@
     AttAdBase getYwxtResInfo(String cityName, String countyName);
 
 	List<AttResBaseUserExcel> exportAttResBaseUser(Map<String, Object> params);
+
+	List<AttResWithRainfall> getAttResByRainfall(String minRainfall, String maxRainfall, String dayIndex) ;
+
+	int[] getAttResNumByRainfall(String dayIndex);
 }
diff --git a/skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/service/impl/AttResBaseServiceImpl.java b/skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/service/impl/AttResBaseServiceImpl.java
index 183a1dc..77be4f6 100644
--- a/skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/service/impl/AttResBaseServiceImpl.java
+++ b/skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/service/impl/AttResBaseServiceImpl.java
@@ -1,6 +1,7 @@
 package cn.gistack.sm.sjztmd.service.impl;
 
 import cn.gistack.sm.intelligentCall.mapper.CallTaskMapper;
+import cn.gistack.sm.sjztmd.constant.RainfallConstant;
 import cn.gistack.sm.sjztmd.dto.AttResBaseUserDTO;
 import cn.gistack.sm.sjztmd.entity.AttAdBase;
 import cn.gistack.sm.sjztmd.entity.AttResBase;
@@ -11,9 +12,20 @@
 import cn.gistack.sm.sjztmd.service.IRelResLabelService;
 import cn.gistack.sm.sjztmd.vo.*;
 import cn.gistack.system.feign.ISysClient;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.StrUtil;
 import com.baomidou.dynamic.datasource.annotation.DS;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpResponse;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springblade.core.log.exception.ServiceException;
 import org.springblade.core.secure.BladeUser;
 import org.springblade.core.secure.utils.AuthUtil;
 import org.springblade.core.tool.utils.DateUtil;
@@ -23,6 +35,7 @@
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.RequestParam;
 import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * @ClassName AttResBaseServiceImpl
@@ -31,9 +44,12 @@
  * @Date 2023/4/21 20:01
  * @Version 1.0
  */
+
+@Slf4j
 @Service
 @DS("zt")
 public class AttResBaseServiceImpl extends ServiceImpl<AttResBaseMapper, AttResBase> implements IAttResBaseService {
+	private static final ObjectMapper objectMapper = new ObjectMapper();
 
 	@Autowired
 	private CallTaskMapper callTaskMapper;
@@ -99,6 +115,138 @@
 	}
 
 	@Override
+	public List<AttResWithRainfall> getAttResByRainfall(String minRainfall, String maxRainfall, String dayIndex) {
+		if (StrUtil.isNotEmpty(dayIndex) && !Arrays.asList(0,1,2).contains(Integer.parseInt(dayIndex))) {
+			throw new ServiceException("无效的dayIndex");
+		}
+		Double minRainfallDouble = null;
+		Double maxRainfallDouble = null;
+		if (StrUtil.isNotEmpty(minRainfall)) {
+			minRainfallDouble = Double.parseDouble(minRainfall);
+		}
+		if (StrUtil.isNotEmpty(minRainfall)) {
+			maxRainfallDouble = Double.parseDouble(maxRainfall);
+		}
+		// 调用气象预报服务接口
+		String resJsonNodeStr;
+		try (HttpResponse httpResponse = HttpRequest.post(RainfallConstant.FORECAST_SERVICE_URL)
+			.header("Content-Type", "application/json")
+			.header("X-token", RainfallConstant.X_TOKEN)
+			.contentType("application/json")
+			.body(String.format(RainfallConstant.REQUEST_JSON_BODY, StrUtil.isEmpty(dayIndex) ? "0" : dayIndex)).execute()) {
+			JsonNode resJsonNode = objectMapper.readTree(httpResponse.body());
+			resJsonNodeStr = resJsonNode.toString();
+		} catch (Exception e) {
+			throw new ServiceException("调用气象预报服务接口失败,详情:" + e.getMessage());
+		}
+		RainfallGridData gridData;
+		try {
+			gridData = objectMapper.readValue(resJsonNodeStr, RainfallGridData.class);
+		} catch (JsonProcessingException e) {
+			throw new ServiceException("转换降雨栅格数据失败,详情:" + e.getMessage());
+		}
+		RainfallGridData.Data data = gridData.getData();
+		RainfallGridData.GridInfo gridInfo = data.getGridInfo();
+		List<List<Double>> gridDataList = data.getGridData();
+		if (gridDataList == null || gridDataList.isEmpty()) {
+			log.warn("gridDataList为空!");
+			return new ArrayList<>();
+		}
+		// 获取网格信息
+		int lonNum = gridInfo.getLonNum();    // 网格横向(经度)
+		double lonMax = gridInfo.getLonMax();
+		double lonSpan = gridInfo.getLonSpan();  // 每个网格单元的经度跨度
+		double latSpan = gridInfo.getLatSpan();   // 每个网格单元的纬度跨度
+		double latMin = gridInfo.getLatMin();    // 网格的最小纬度
+		double lonMin = gridInfo.getLonMin();   // 网格的最小经度
+		double latMax = gridInfo.getLatMax();
+		int latNum = gridInfo.getLatNum();    // 网格纵向(纬度)
+
+		// 将网格数据转换为二维数组
+		double[][] grid = new double[latNum][lonNum];
+		for (int i = 0; i < latNum; i++) {
+			List<Double> row = gridDataList.get(i);
+			for (int j = 0; j < lonNum; j++) {
+				grid[i][j] = row.get(j);
+			}
+		}
+
+		LambdaQueryWrapper<AttResBase> queryWrapper = Wrappers.<AttResBase>query().lambda();
+		List<AttResBase> attResBases = baseMapper.selectList(queryWrapper);
+	    List<AttResWithRainfall> attResWithRainfallList = new ArrayList<>();
+
+		for (AttResBase attResBase : attResBases) {
+			// 横向索引(lonIndex):(站点的经度 - 网格最小经度) / 每个网格单元的经度跨度
+			int lonIndex = (int) ((Double.parseDouble(attResBase.getCenterLong()) - lonMin) / lonSpan);
+			//  纵向索引(latIndex):(站点的纬度 - 网格最小纬度) / 每个网格单元的纬度跨度
+			int latIndex = (int) ((Double.parseDouble(attResBase.getCenterLat()) - latMin) / latSpan);
+			if (lonIndex >= 0 && lonIndex < lonNum && latIndex >= 0 && latIndex < latNum) {
+				double rainfall = grid[latIndex][lonIndex];
+				// 降雨量 <= 最小值的站点,都过滤掉, 降雨量为0除外
+				if (null != minRainfallDouble &&  rainfall != 0 && rainfall <= minRainfallDouble) {
+					continue;
+				}
+				// 降雨量 > 最大值的站点,都过滤掉
+				if (null != maxRainfallDouble &&  rainfall > maxRainfallDouble) {
+					continue;
+				}
+				AttResWithRainfall attResWithRainfall = new AttResWithRainfall();
+				BeanUtil.copyProperties(attResBase, attResWithRainfall);
+				attResWithRainfall.setRainfall(String.valueOf(rainfall));
+				attResWithRainfallList.add(attResWithRainfall);
+			} else {
+				log.warn("AttRes {} is out of the grid range. centerLong: {}, centerLat: {}", attResBase.getName(),
+						attResBase.getCenterLong(), attResBase.getCenterLat());
+			}
+		}
+		return attResWithRainfallList;
+	}
+
+	@Override
+	public int[] getAttResNumByRainfall(String dayIndex) {
+		List<AttResWithRainfall> attResByRainfall;
+		// 获取站点降雨量失败或者返回站点列表为空时,直接返回空数组
+		try {
+			attResByRainfall = getAttResByRainfall(null, null, dayIndex);
+		} catch (Exception e) {
+			return RainfallConstant.EMPTY_ATT_RES_NUM_ARRAY;
+		}
+		if (attResByRainfall == null || attResByRainfall.isEmpty()) {
+			return RainfallConstant.EMPTY_ATT_RES_NUM_ARRAY;
+		}
+		// 降雨范围-站点数量
+		Map<String, Integer> rainfallRangeAttResNumMap = attResByRainfall.stream()
+			.collect(Collectors.groupingBy(attResWithRainfall -> {
+				int rainfall = Integer.parseInt(attResWithRainfall.getRainfall());
+				if (rainfall > 250) {
+					return "(250,+)";
+				} else if (rainfall > 100) {
+					return "(100,250]";
+				} else if (rainfall > 50) {
+					return "(50,100]";
+				} else if (rainfall > 25) {
+					return "(25,50]";
+				} else if (rainfall > 10) {
+					return "(10,25]";
+				} else if (rainfall > 5) {
+					return "(5,10]";
+				} else if (rainfall > 0.1) {
+					return "(0.1,5]";
+				} else if (rainfall >= 0) {
+					return "[0,0.1]";
+				} else {
+					throw new ServiceException("无效的降雨值:" + rainfall);
+				}
+			}, Collectors.collectingAndThen(Collectors.counting(), Long::intValue)));
+		int[] attResNumArray = new int[RainfallConstant.RAIN_FALL_RANGES.size()];
+		for (int i = 0; i < RainfallConstant.RAIN_FALL_RANGES.size(); i++) {
+			String range = RainfallConstant.RAIN_FALL_RANGES.get(i);
+			attResNumArray[i] = rainfallRangeAttResNumMap.getOrDefault(range, 0);
+		}
+		return attResNumArray;
+	}
+
+	@Override
 	@Transactional(rollbackFor = Exception.class)
 	public Boolean updateResBaseByGuid(AttResBase attResBase) {
 		attResBase.setUpdateTime(DateUtil.now());
diff --git a/skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/vo/AttResWithRainfall.java b/skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/vo/AttResWithRainfall.java
new file mode 100644
index 0000000..b921fa0
--- /dev/null
+++ b/skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/vo/AttResWithRainfall.java
@@ -0,0 +1,14 @@
+package cn.gistack.sm.sjztmd.vo;
+
+import cn.gistack.sm.sjztmd.entity.AttResBase;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class AttResWithRainfall extends AttResBase {
+
+    @ApiModelProperty(value = "降雨量")
+    private String rainfall;
+}
diff --git a/skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/vo/RainfallGridData.java b/skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/vo/RainfallGridData.java
new file mode 100644
index 0000000..fdeff6a
--- /dev/null
+++ b/skjcmanager/skjcmanager-service/skjcmanager-sm/src/main/java/cn/gistack/sm/sjztmd/vo/RainfallGridData.java
@@ -0,0 +1,43 @@
+package cn.gistack.sm.sjztmd.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class RainfallGridData {
+    private String code;
+
+    private String msg;
+
+    private Data data;
+
+    @lombok.Data
+    public static class Data {
+        private String fileName;
+
+        private GridInfo gridInfo;
+
+        private List<List<Double>> gridData;
+
+    }
+
+    @lombok.Data
+    public static class GridInfo {
+        private int lonNum;
+
+        private double lonMax;
+
+        private double lonSpan;
+
+        private double latSpan;
+
+        private double latMin;
+
+        private double lonMin;
+
+        private double latMax;
+
+        private int latNum;
+    }
+}

--
Gitblit v1.9.3