From b09e39ad120086b67371e2d9ac03231cd6c5b398 Mon Sep 17 00:00:00 2001 From: jinpengyong <jpy123456> Date: Tue, 04 Jan 2022 11:10:39 +0800 Subject: [PATCH] 臭氧预测 --- screen-api/src/main/java/com/moral/api/service/impl/HistoryHourlyServiceImpl.java | 8 screen-job/src/main/java/com/moral/api/mapper/ForecastMapper.java | 16 ++ screen-common/src/main/java/com/moral/util/DateUtils.java | 36 +++- screen-job/src/main/java/com/moral/api/service/impl/ForecastServiceImpl.java | 245 ++++++++++++++++++++++++++++++ screen-job/src/main/java/com/moral/api/entity/Forecast.java | 45 +++++ screen-api/src/main/java/com/moral/api/service/impl/HistoryDailyServiceImpl.java | 8 screen-job/src/main/java/com/moral/api/service/ForecastService.java | 18 ++ screen-api/src/main/java/com/moral/api/service/impl/HistoryMonthlyServiceImpl.java | 8 screen-job/src/main/java/com/moral/api/task/ForecastTask.java | 27 +++ screen-job/src/main/resources/mapper/ForecastMapper.xml | 12 + 10 files changed, 402 insertions(+), 21 deletions(-) diff --git a/screen-api/src/main/java/com/moral/api/service/impl/HistoryDailyServiceImpl.java b/screen-api/src/main/java/com/moral/api/service/impl/HistoryDailyServiceImpl.java index c976a62..58a61b2 100644 --- a/screen-api/src/main/java/com/moral/api/service/impl/HistoryDailyServiceImpl.java +++ b/screen-api/src/main/java/com/moral/api/service/impl/HistoryDailyServiceImpl.java @@ -366,13 +366,13 @@ result = doubleStream.sum(); } else { if ("min".equals(type)) { - optionalDouble = doubleStream.average(); - - } else if ("max".equals(type)) { optionalDouble = doubleStream.min(); - } else if ("avg".equals(type)) { + } else if ("max".equals(type)) { optionalDouble = doubleStream.max(); + + } else if ("avg".equals(type)) { + optionalDouble = doubleStream.average(); } if (optionalDouble.isPresent()) { diff --git a/screen-api/src/main/java/com/moral/api/service/impl/HistoryHourlyServiceImpl.java b/screen-api/src/main/java/com/moral/api/service/impl/HistoryHourlyServiceImpl.java index 6715169..734b4dd 100644 --- a/screen-api/src/main/java/com/moral/api/service/impl/HistoryHourlyServiceImpl.java +++ b/screen-api/src/main/java/com/moral/api/service/impl/HistoryHourlyServiceImpl.java @@ -651,13 +651,13 @@ result = doubleStream.sum(); } else { if ("min".equals(type)) { - optionalDouble = doubleStream.average(); - - } else if ("max".equals(type)) { optionalDouble = doubleStream.min(); - } else if ("avg".equals(type)) { + } else if ("max".equals(type)) { optionalDouble = doubleStream.max(); + + } else if ("avg".equals(type)) { + optionalDouble = doubleStream.average(); } if (optionalDouble.isPresent()) { diff --git a/screen-api/src/main/java/com/moral/api/service/impl/HistoryMonthlyServiceImpl.java b/screen-api/src/main/java/com/moral/api/service/impl/HistoryMonthlyServiceImpl.java index 613a00c..e227257 100644 --- a/screen-api/src/main/java/com/moral/api/service/impl/HistoryMonthlyServiceImpl.java +++ b/screen-api/src/main/java/com/moral/api/service/impl/HistoryMonthlyServiceImpl.java @@ -331,13 +331,13 @@ result = doubleStream.sum(); } else { if ("min".equals(type)) { - optionalDouble = doubleStream.average(); - - } else if ("max".equals(type)) { optionalDouble = doubleStream.min(); - } else if ("avg".equals(type)) { + } else if ("max".equals(type)) { optionalDouble = doubleStream.max(); + + } else if ("avg".equals(type)) { + optionalDouble = doubleStream.average(); } if (optionalDouble.isPresent()) { diff --git a/screen-common/src/main/java/com/moral/util/DateUtils.java b/screen-common/src/main/java/com/moral/util/DateUtils.java index 7776593..2741da9 100644 --- a/screen-common/src/main/java/com/moral/util/DateUtils.java +++ b/screen-common/src/main/java/com/moral/util/DateUtils.java @@ -956,9 +956,9 @@ cal.setTime(date); final int last = cal.getActualMinimum(Calendar.DAY_OF_YEAR); cal.set(Calendar.DAY_OF_YEAR, last); - cal.set(Calendar.HOUR_OF_DAY,0); - cal.set(Calendar.MINUTE,0); - cal.set(Calendar.SECOND,0); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); return cal.getTime(); } @@ -977,16 +977,16 @@ } /* - * ��������������������������������� - * */ - public static Date getFirstDayOfMonth(Date date){ + * ��������������������������������� + * */ + public static Date getFirstDayOfMonth(Date date) { final Calendar cal = Calendar.getInstance(); cal.setTime(date); final int last = cal.getActualMinimum(Calendar.DAY_OF_MONTH); cal.set(Calendar.DAY_OF_MONTH, last); - cal.set(Calendar.HOUR_OF_DAY,0); - cal.set(Calendar.MINUTE,0); - cal.set(Calendar.SECOND,0); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); return cal.getTime(); } @@ -1796,4 +1796,22 @@ String lastYear = getDateAddYear(DateUtils.dateToDateString(getDate(), DateUtils.yyyy), -1); return DateUtils.getDate(lastYear, DateUtils.yyyy); } + + //��������������������������������������������������� + public static Map<Date, List<Integer>> getBeforeAndAfterHourDate(Date date) { + String s = dateToDateString(date, yyyy_MM_dd_EN); + List<String> timeLag = getTimeLag(s); + Map<Date, List<Integer>> result = new HashMap<>(); + for (String s1 : timeLag) { + List<Integer> objects = new ArrayList<>(); + Date current = getDate(s1, yyyy_MM_dd_HH_EN); + Date before = addHours(current, -1); + Date after = addHours(current, 1); + objects.add(getHour(before)); + objects.add(getHour(current)); + objects.add(getHour(after)); + result.put(current, objects); + } + return result; + } } diff --git a/screen-job/src/main/java/com/moral/api/entity/Forecast.java b/screen-job/src/main/java/com/moral/api/entity/Forecast.java new file mode 100644 index 0000000..3b0e4e8 --- /dev/null +++ b/screen-job/src/main/java/com/moral/api/entity/Forecast.java @@ -0,0 +1,45 @@ +package com.moral.api.entity; + +import com.baomidou.mybatisplus.extension.activerecord.Model; +import java.io.Serializable; +import java.util.Date; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * <p> + * ������������������ + * </p> + * + * @author moral + * @since 2021-12-31 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class Forecast extends Model<Forecast> { + + private static final long serialVersionUID = 1L; + + /** + * ������id + */ + private Integer cityCode; + + /** + * ������ + */ + private Date time; + + /** + * ������ + */ + private String value; + + + @Override + protected Serializable pkVal() { + return null; + } + +} diff --git a/screen-job/src/main/java/com/moral/api/mapper/ForecastMapper.java b/screen-job/src/main/java/com/moral/api/mapper/ForecastMapper.java new file mode 100644 index 0000000..c8f0e87 --- /dev/null +++ b/screen-job/src/main/java/com/moral/api/mapper/ForecastMapper.java @@ -0,0 +1,16 @@ +package com.moral.api.mapper; + +import com.moral.api.entity.Forecast; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * <p> + * ������������������ Mapper ������ + * </p> + * + * @author moral + * @since 2021-12-31 + */ +public interface ForecastMapper extends BaseMapper<Forecast> { + +} diff --git a/screen-job/src/main/java/com/moral/api/service/ForecastService.java b/screen-job/src/main/java/com/moral/api/service/ForecastService.java new file mode 100644 index 0000000..eb3aeb6 --- /dev/null +++ b/screen-job/src/main/java/com/moral/api/service/ForecastService.java @@ -0,0 +1,18 @@ +package com.moral.api.service; + +import com.moral.api.entity.Forecast; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * <p> + * ������������������ ��������� + * </p> + * + * @author moral + * @since 2021-12-31 + */ +public interface ForecastService extends IService<Forecast> { + + void forecastO3(); + +} diff --git a/screen-job/src/main/java/com/moral/api/service/impl/ForecastServiceImpl.java b/screen-job/src/main/java/com/moral/api/service/impl/ForecastServiceImpl.java new file mode 100644 index 0000000..944dd08 --- /dev/null +++ b/screen-job/src/main/java/com/moral/api/service/impl/ForecastServiceImpl.java @@ -0,0 +1,245 @@ +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.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.OptionalDouble; +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 ForecastService forecastService; + + private static 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); + + Forecast forecast = new Forecast(); + Map<String, Object> forecastMap = new HashMap<>(); + for (Object obj : cityCodes) { + Integer cityCode = Integer.parseInt(obj.toString()); + + forecast.setCityCode(cityCode); + + //������ + List<CityWeatherForecast> cityWeatherForecasts = 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) { + 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)); + forecastService.save(forecast); + } + } + + } + } + + 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(); + } + +} diff --git a/screen-job/src/main/java/com/moral/api/task/ForecastTask.java b/screen-job/src/main/java/com/moral/api/task/ForecastTask.java new file mode 100644 index 0000000..b4a236a --- /dev/null +++ b/screen-job/src/main/java/com/moral/api/task/ForecastTask.java @@ -0,0 +1,27 @@ +package com.moral.api.task; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.moral.api.service.ForecastService; +import com.xxl.job.core.biz.model.ReturnT; +import com.xxl.job.core.context.XxlJobHelper; +import com.xxl.job.core.handler.annotation.XxlJob; + +@Component +public class ForecastTask { + + @Autowired + private ForecastService forecastService; + + @XxlJob("forecastO3") + public ReturnT forecastO3() { + try { + forecastService.forecastO3(); + } catch (Exception e) { + XxlJobHelper.log(e.getMessage()); + return new ReturnT(ReturnT.FAIL_CODE, e.getMessage()); + } + return ReturnT.SUCCESS; + } +} diff --git a/screen-job/src/main/resources/mapper/ForecastMapper.xml b/screen-job/src/main/resources/mapper/ForecastMapper.xml new file mode 100644 index 0000000..311b519 --- /dev/null +++ b/screen-job/src/main/resources/mapper/ForecastMapper.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="com.moral.api.mapper.ForecastMapper"> + + <!-- ������������������������ --> + <resultMap id="BaseResultMap" type="com.moral.api.entity.Forecast"> + <result column="city_code" property="cityCode"/> + <result column="time" property="time"/> + <result column="value" property="value"/> + </resultMap> + +</mapper> \ No newline at end of file -- Gitblit v1.8.0