| package com.moral.api.service.impl; | 
|   | 
| import com.alibaba.fastjson.JSONObject; | 
| import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | 
| import com.moral.api.entity.CityAqi; | 
| import com.moral.api.entity.CityConfigWeatherForecast; | 
| import com.moral.api.entity.CityWeather; | 
| import com.moral.api.entity.CityWeatherForecast; | 
| import com.moral.api.entity.Forecast; | 
| import com.moral.api.mapper.ForecastMapper; | 
| import com.moral.api.service.CityAqiService; | 
| import com.moral.api.service.CityConfigWeatherForecastService; | 
| import com.moral.api.service.CityWeatherForecastService; | 
| import com.moral.api.service.CityWeatherService; | 
| import com.moral.api.service.ForecastService; | 
| import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | 
| import com.moral.constant.Constants; | 
| import com.moral.util.AmendUtils; | 
| import com.moral.util.DateUtils; | 
|   | 
| import org.springframework.beans.factory.annotation.Autowired; | 
| import org.springframework.stereotype.Service; | 
| import org.springframework.util.ObjectUtils; | 
|   | 
| import java.util.*; | 
| import java.util.function.Supplier; | 
| import java.util.stream.Collectors; | 
| import java.util.stream.DoubleStream; | 
| import java.util.stream.Stream; | 
|   | 
| /** | 
|  * <p> | 
|  * 预测小时数据 服务实现类 | 
|  * </p> | 
|  * | 
|  * @author moral | 
|  * @since 2021-12-31 | 
|  */ | 
| @Service | 
| public class ForecastServiceImpl extends ServiceImpl<ForecastMapper, Forecast> implements ForecastService { | 
|   | 
|     @Autowired | 
|     private CityWeatherForecastService cityWeatherForecastService; | 
|   | 
|     @Autowired | 
|     private CityConfigWeatherForecastService cityConfigWeatherForecastService; | 
|   | 
|     @Autowired | 
|     private CityWeatherService cityWeatherService; | 
|   | 
|     @Autowired | 
|     private CityAqiService cityAqiService; | 
|   | 
|     @Autowired | 
|     private ForecastMapper forecastMapper; | 
|   | 
|     private static final Map<String, Integer> weatherScore = new HashMap<>(); | 
|   | 
|     static { | 
|         weatherScore.put("晴", 100); | 
|         weatherScore.put("雾", 90); | 
|         weatherScore.put("多云", 80); | 
|         weatherScore.put("霾", 70); | 
|         weatherScore.put("阴", 60); | 
|         weatherScore.put("扬沙", 60); | 
|         weatherScore.put("浮尘", 60); | 
|         weatherScore.put("阵雨", 45); | 
|         weatherScore.put("雷阵雨", 40); | 
|         weatherScore.put("雷阵雨转小雨", 30); | 
|         weatherScore.put("阵雨转小雨", 30); | 
|         weatherScore.put("小雨", 20); | 
|         weatherScore.put("雨", 10); | 
|         weatherScore.put("雷阵雨转中雨", 5); | 
|         weatherScore.put("雷阵雨转大雨", 4); | 
|         weatherScore.put("中雨", 0); | 
|         weatherScore.put("大雨", 0); | 
|         weatherScore.put("暴雨", 0); | 
|         weatherScore.put("小雪", 0); | 
|         weatherScore.put("中雪", 0); | 
|         weatherScore.put("大雪", 0); | 
|         weatherScore.put("暴雪", 0); | 
|         weatherScore.put("雨夹雪", 0); | 
|     } | 
|   | 
|     @Override | 
|     public void forecastO3() { | 
|         Date nextDay = DateUtils.addDays(new Date(), 1); | 
|         String nextTime = DateUtils.dateToDateString(nextDay, DateUtils.yyyy_MM_dd_EN); | 
|         //根据最近一个月历史数据来预测 | 
|         Date start = DateUtils.addMonths(DateUtils.getDate(nextTime), -1); | 
|   | 
|         //获取要预测的城市 | 
|         QueryWrapper<CityConfigWeatherForecast> cityConfigWeatherForecastQueryWrapper = new QueryWrapper<>(); | 
|         cityConfigWeatherForecastQueryWrapper.select("city_code") | 
|                 .eq("is_delete", Constants.NOT_DELETE); | 
|         List<Object> cityCodes = cityConfigWeatherForecastService.listObjs(cityConfigWeatherForecastQueryWrapper); | 
|   | 
|   | 
|         //获取预测日逐小时气象数据 | 
|         QueryWrapper<CityWeatherForecast> cityWeatherForecastQueryWrapper = new QueryWrapper<>(); | 
|         cityWeatherForecastQueryWrapper.likeRight("time", nextTime) | 
|                 .in("city_code", cityCodes); | 
|         List<CityWeatherForecast> forecasts = cityWeatherForecastService.list(cityWeatherForecastQueryWrapper); | 
|         Map<Integer, List<CityWeatherForecast>> cityForecast = forecasts.stream() | 
|                 .collect(Collectors.groupingBy(CityWeatherForecast::getCityCode)); | 
|   | 
|   | 
|         //获取近两个月历史气象数据 | 
|         QueryWrapper<CityWeather> cityWeatherQueryWrapper = new QueryWrapper<>(); | 
|         cityWeatherQueryWrapper.ge("time", start) | 
|                 .in("city_code", cityCodes); | 
|         List<CityWeather> historyWeather = cityWeatherService.list(cityWeatherQueryWrapper); | 
|         Map<Integer, List<CityWeather>> cityHistoryWeather = historyWeather.stream() | 
|                 .collect(Collectors.groupingBy(CityWeather::getCityCode)); | 
|   | 
|         //获取近两个月历史aqi数据 | 
|         QueryWrapper<CityAqi> cityAqiQueryWrapper = new QueryWrapper<>(); | 
|         cityAqiQueryWrapper.ge("time", start) | 
|                 .in("city_code", cityCodes); | 
|         List<CityAqi> historyAqi = cityAqiService.list(cityAqiQueryWrapper); | 
|         Map<Integer, List<CityAqi>> cityHistoryAqi = historyAqi.stream() | 
|                 .collect(Collectors.groupingBy(CityAqi::getCityCode)); | 
|   | 
|         //获取前后一小时map | 
|         Map<Date, List<Integer>> hours = DateUtils.getBeforeAndAfterHourDate(nextDay); | 
|   | 
|         List<Forecast> forecastList = new ArrayList<>(); | 
|   | 
|         Map<String, Object> forecastMap = new HashMap<>(); | 
|         for (Object obj : cityCodes) { | 
|             Integer cityCode = Integer.parseInt(obj.toString()); | 
|   | 
|             //预测 | 
|             List<CityWeatherForecast> cityWeatherForecasts = Objects.isNull(cityForecast.get(cityCode))?new ArrayList<>():cityForecast.get(cityCode); | 
|   | 
|             //获取城市历史气象数据 | 
|             List<CityWeather> cityWeathers = cityHistoryWeather.get(cityCode); | 
|             //获取城市历史aqi数据 | 
|             List<CityAqi> cityAqis = cityHistoryAqi.get(cityCode); | 
|             Map<Date, List<CityAqi>> cityAqiMap = cityAqis.stream() | 
|                     .collect(Collectors.groupingBy(CityAqi::getTime)); | 
|   | 
|             Map<Date, List<Map<String, Object>>> cityHistoryMap = new HashMap<>(); | 
|   | 
|             for (Map.Entry<Date, List<Integer>> dateListEntry : hours.entrySet()) { | 
|                 Date date = dateListEntry.getKey(); | 
|                 List<Integer> hourList = dateListEntry.getValue(); | 
|                 List<Map<String, Object>> hourWeatherData = new ArrayList<>(); | 
|                 for (CityWeather cityWeather : cityWeathers) { | 
|                     Date cityWeatherTime = cityWeather.getTime(); | 
|                     int dataHour = DateUtils.getHour(cityWeatherTime); | 
|                     if (hourList.contains(dataHour)) { | 
|                         Map<String, Object> valueMap = JSONObject.parseObject(cityWeather.getValue(), Map.class); | 
|                         List<CityAqi> o = cityAqiMap.get(cityWeatherTime); | 
|                         if (!ObjectUtils.isEmpty(o)) { | 
|                             String value = o.get(0).getValue(); | 
|                             Map<String, Object> aqiMap = JSONObject.parseObject(value, Map.class); | 
|                             valueMap.put("O3", aqiMap.get("O3")); | 
|                             hourWeatherData.add(valueMap); | 
|                         } | 
|                     } | 
|                 } | 
|                 cityHistoryMap.put(date, hourWeatherData); | 
|             } | 
|   | 
|             for (CityWeatherForecast cityWeatherForecast : cityWeatherForecasts) { | 
|                 Forecast forecast = new Forecast(); | 
|                 forecast.setCityCode(cityCode); | 
|                 Date time = cityWeatherForecast.getTime(); | 
|                 Map<String, Object> value = JSONObject.parseObject(cityWeatherForecast.getValue(), Map.class); | 
|                 String weather = value.get("text").toString(); | 
|                 Integer forecastScore = weatherScore.get(weather); | 
|                 int min; | 
|                 if (forecastScore >= 80) { | 
|                     min = 80; | 
|                 } else if (forecastScore >= 40) { | 
|                     min = 40; | 
|                 } else { | 
|                     min = 0; | 
|                 } | 
|                 //气象 | 
|                 List<Map<String, Object>> weatherMaps = cityHistoryMap.get(time); | 
|   | 
|                 //根据天气分数筛选 | 
|                 weatherMaps.removeIf(o -> { | 
|                     Integer historyScore = weatherScore.get(o.get("text").toString()); | 
|                     return historyScore < min; | 
|                 }); | 
|                 int size = weatherMaps.size(); | 
|                 Double tempAvg = calculateAvg(weatherMaps, "temp"); | 
|                 Double o3Avg = calculateAvg(weatherMaps, "O3"); | 
|                 Double sum1 = calculateProduct(weatherMaps, "temp", "O3"); | 
|                 Double sum2 = calculateProduct(weatherMaps, "temp", "temp"); | 
|                 double b = (sum1 - size * tempAvg * o3Avg) / (sum2 - size * tempAvg * tempAvg); | 
|                 double a = o3Avg - b * tempAvg; | 
|                 double tempForecast = Double.parseDouble(value.get("temp").toString()); | 
|                 double o3Forecast = b * tempForecast + a; | 
|   | 
|                 forecast.setTime(cityWeatherForecast.getTime()); | 
|   | 
|                 if (!Double.isNaN(o3Forecast)) { | 
|                     forecastMap.put("O3", AmendUtils.sciCal(o3Forecast, 0)); | 
|                     forecast.setValue(JSONObject.toJSONString(forecastMap)); | 
|                     forecastList.add(forecast); | 
|                 } | 
|             } | 
|         } | 
|         if(forecastList.size()>0){ | 
|             forecastMapper.insertForecast(forecastList); | 
|         } | 
|   | 
|     } | 
|   | 
|     private Double calculateAvg(List<Map<String, Object>> list, String param) { | 
|         Supplier<Stream<Map<String, Object>>> supplier = list::stream; | 
|         DoubleStream doubleStream = supplier.get() | 
|                 .flatMapToDouble(value -> { | 
|                     String sensorValue = value.get(param).toString(); | 
|                     double paramValue = Double.parseDouble(sensorValue); | 
|                     return DoubleStream.of(paramValue); | 
|                 }); | 
|         Double result = null; | 
|         OptionalDouble optionalDouble = doubleStream.average(); | 
|         if (optionalDouble.isPresent()) { | 
|             result = optionalDouble.getAsDouble(); | 
|         } | 
|         return result; | 
|   | 
|     } | 
|   | 
|     private Double calculateProduct(List<Map<String, Object>> list, String param1, String param2) { | 
|         Supplier<Stream<Map<String, Object>>> supplier = list::stream; | 
|         DoubleStream doubleStream = supplier.get() | 
|                 .flatMapToDouble(value -> { | 
|                     String sensorValue1 = value.get(param1).toString(); | 
|                     String sensorValue2 = value.get(param2).toString(); | 
|                     double paramValue1 = Double.parseDouble(sensorValue1); | 
|                     double paramValue2 = Double.parseDouble(sensorValue2); | 
|                     return DoubleStream.of(paramValue1 * paramValue2); | 
|                 }); | 
|         return doubleStream.sum(); | 
|     } | 
|   | 
| } |