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