package com.moral.api.service.impl;
|
|
import com.alibaba.fastjson.JSON;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.moral.api.config.properties.SpecialCitiesProperties;
|
import com.moral.api.entity.CityAqi;
|
import com.moral.api.entity.CityAqiDaily;
|
import com.moral.api.entity.CityAqiYearly;
|
import com.moral.api.entity.SysArea;
|
import com.moral.api.mapper.CityAqiYearlyMapper;
|
import com.moral.api.pojo.dto.cityAQI.CityPollutionLevel;
|
import com.moral.api.pojo.dto.cityAQI.DataPercentRange;
|
import com.moral.api.service.CityAqiDailyService;
|
import com.moral.api.service.CityAqiYearlyService;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.moral.api.service.SysAreaService;
|
import com.moral.constant.Constants;
|
import com.moral.util.AmendUtils;
|
import com.moral.util.DateUtils;
|
import com.moral.util.MathUtils;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
import org.springframework.util.ObjectUtils;
|
|
import java.text.DateFormat;
|
import java.util.*;
|
|
/**
|
* <p>
|
* 城市aqi年数据表 服务实现类
|
* </p>
|
*
|
* @author moral
|
* @since 2021-11-05
|
*/
|
@Service
|
public class CityAqiYearlyServiceImpl extends ServiceImpl<CityAqiYearlyMapper, CityAqiYearly> implements CityAqiYearlyService {
|
|
@Autowired
|
CityAqiYearlyMapper cityAqiYearlyMapper;
|
@Autowired
|
SpecialCitiesProperties specialCitiesProperties;
|
@Autowired
|
SysAreaService sysAreaService;
|
@Autowired
|
CityAqiDailyService cityAqiDailyService;
|
|
@Override
|
public List<CityAqiYearly> getCityAqiYearlyByRegionCodeAndTime(Integer regionCode, Date startDate, Date endDate) {
|
QueryWrapper<CityAqiYearly> queryWrapper = new QueryWrapper<>();
|
queryWrapper.eq("city_code", regionCode);
|
queryWrapper.between("time", startDate, endDate);
|
return cityAqiYearlyMapper.selectList(queryWrapper);
|
}
|
|
@Override
|
public Map<String, DataPercentRange> analysisPollutionLevel(String year, Integer cityCode) {
|
//判断是否为今年,如果是今年则使用日数据进行查询,否则使用年数据
|
boolean thisYear = isThisYear(year);
|
//获取开始结束时间
|
Date yearDate = DateUtils.getDate(year, "yyyy");
|
Date startDate = DateUtils.getFirstDayOfYear(yearDate);
|
Date endDate;
|
if (thisYear)
|
endDate = DateUtils.addDays(new Date(), -1);
|
else
|
endDate = DateUtils.getLastDayOfYear(yearDate);
|
//判断是否属于2+26城市
|
List<SysArea> twentyEightCities = null;
|
if (specialCitiesProperties.isTwentyEightCities(cityCode))
|
twentyEightCities = specialCitiesProperties.getTwentyEightCities();
|
//判断是否属于河北通道城市
|
List<SysArea> heBeiEightCities = null;
|
if (specialCitiesProperties.isHeBeiEightCities(cityCode))
|
heBeiEightCities = specialCitiesProperties.getHeBeiEightCities();
|
//获取市所在省的所有城市
|
//如果是直辖市则不需要排名
|
List<SysArea> cities = null;
|
if (!cityCode.toString().substring(2).equals("0000")) {
|
String provinceCode = cityCode.toString().substring(0, 2) + "0000";
|
cities = sysAreaService.getChildren(Integer.parseInt(provinceCode));
|
}
|
//获取城市
|
SysArea city = sysAreaService.getAreaByCode(cityCode);
|
//计算优良天数
|
DataPercentRange fineDays = calculateFineDays(cities, twentyEightCities, heBeiEightCities, startDate, endDate, city);
|
//计算6参以及综合指数排名
|
Map<String, DataPercentRange> sixParamMap = calculateSixParam(thisYear, startDate, endDate, city, cities, twentyEightCities, heBeiEightCities);
|
Map<String,DataPercentRange> result = new HashMap<>();
|
if(fineDays!=null)
|
result.put("fineDays",fineDays);
|
if(sixParamMap!=null)
|
result.putAll(sixParamMap);
|
if(result.size()==0)
|
return null;
|
return result;
|
}
|
|
|
//计算优良天数和同比以及省内,28城市,省内通道的排名。
|
private DataPercentRange calculateFineDays(List<SysArea> provinceCities,
|
List<SysArea> twentyEightCities,
|
List<SysArea> heBeiEightCities,
|
Date startDate,
|
Date endDate,
|
SysArea city) {
|
DataPercentRange dataPercentRange = new DataPercentRange();
|
|
//查询优良天气数
|
CityPollutionLevel cityPollutionLevel = cityAqiDailyService.calculateDaysByTimeAndSysArea(city, startDate, endDate);
|
if(cityPollutionLevel==null)
|
return null;
|
Integer fineDays = cityPollutionLevel.getExcellentWeatherDays() + cityPollutionLevel.getGoodWeatherDays();
|
//计算同比数据
|
Date compareStartDate = DateUtils.addMonths(startDate, -12);
|
Date compareEndDate = DateUtils.addMonths(endDate, -12);
|
CityPollutionLevel cityPollutionLevelCompare = cityAqiDailyService.calculateDaysByTimeAndSysArea(city, compareStartDate, compareEndDate);
|
String percent = null;
|
if(cityPollutionLevelCompare!=null){
|
Integer compareFineDays = cityPollutionLevelCompare.getExcellentWeatherDays() + cityPollutionLevelCompare.getGoodWeatherDays();
|
percent = (fineDays - compareFineDays) + " 天";
|
}
|
//计算省内排名
|
Integer provinceRange = null;
|
if(provinceCities!=null)
|
provinceRange = calculateFineDaysRange(provinceCities, city, startDate, endDate);
|
//计算2+26城市排名
|
Integer twentyEightRange = null;
|
if (twentyEightCities != null)
|
twentyEightRange = calculateFineDaysRange(twentyEightCities, city, startDate, endDate);
|
//计算河北省内通道排名
|
Integer provinceChannelRange = null;
|
if (heBeiEightCities != null)
|
provinceChannelRange = calculateFineDaysRange(heBeiEightCities, city, startDate, endDate);
|
dataPercentRange.setConcentration(fineDays + " 天");
|
dataPercentRange.setProvinceChannelRange(provinceChannelRange);
|
dataPercentRange.setTwentyEightCitiesRange(twentyEightRange);
|
dataPercentRange.setProvinceRange(provinceRange);
|
dataPercentRange.setPercent(percent);
|
return dataPercentRange;
|
}
|
|
|
//计算六参以及综指平均浓度以及同比数据和排名情况
|
private Map<String, DataPercentRange> calculateSixParam(boolean thisYear,
|
Date startDate,
|
Date endDate,
|
SysArea city,
|
List<SysArea> provinceCities,
|
List<SysArea> twentyEightCities,
|
List<SysArea> heBeiEightCities) {
|
Integer cityCode = city.getAreaCode();
|
Map<String, DataPercentRange> result = new HashMap<>();
|
//查询平均浓度,判断是否为今年,如果不是今年则用年数据进行计算,是今年则用天数据进行计算
|
Map<String, Double> concentration = getConcentration(thisYear, startDate, endDate, cityCode);
|
if (concentration == null)
|
return null;
|
//查询同比数据
|
Date compareStartDate = DateUtils.addMonths(startDate, -12);
|
Date compareEndDate = DateUtils.addMonths(endDate, -12);
|
Map<String, Double> compareCentration = getConcentration(thisYear, compareStartDate, compareEndDate, cityCode);
|
//计算6参以及综指同比结果
|
Map<String, String> compareResult = null;
|
if(compareCentration!=null)
|
compareResult = calculateSixParamYOY(concentration, compareCentration);
|
//计算省内排名
|
Map<String, Integer> provinceRangeResult = null;
|
if (provinceCities != null)
|
provinceRangeResult = sixParamRange(provinceCities, city, startDate, endDate, thisYear);
|
//计算2+26排名
|
Map<String, Integer> twentyEightRangeResult = null;
|
if (twentyEightCities != null)
|
twentyEightRangeResult = sixParamRange(twentyEightCities, city, startDate, endDate, thisYear);
|
//计算省内通道排名
|
Map<String, Integer> provinceChannelRangeResult = null;
|
if (heBeiEightCities != null)
|
provinceChannelRangeResult = sixParamRange(heBeiEightCities, city, startDate, endDate, thisYear);
|
//封装返回数据
|
DataPercentRange pm2_5 = new DataPercentRange();
|
pm2_5.setConcentration(concentration.get("PM2_5").intValue() + " μg/m³");
|
DataPercentRange PM2_5 = packageSixParam(concentration, compareResult, provinceRangeResult, twentyEightRangeResult, provinceChannelRangeResult, "PM2_5", 0, "μg/m");
|
DataPercentRange PM10 = packageSixParam(concentration, compareResult, provinceRangeResult, twentyEightRangeResult, provinceChannelRangeResult, "PM10", 0, "μg/m");
|
DataPercentRange SO2 = packageSixParam(concentration, compareResult, provinceRangeResult, twentyEightRangeResult, provinceChannelRangeResult, "SO2", 0, "μg/m");
|
DataPercentRange NO2 = packageSixParam(concentration, compareResult, provinceRangeResult, twentyEightRangeResult, provinceChannelRangeResult, "NO2", 0, "μg/m");
|
DataPercentRange O3 = packageSixParam(concentration, compareResult, provinceRangeResult, twentyEightRangeResult, provinceChannelRangeResult, "O3", 0, "μg/m");
|
DataPercentRange CO = packageSixParam(concentration, compareResult, provinceRangeResult, twentyEightRangeResult, provinceChannelRangeResult, "CO", 1, "mg/m");
|
DataPercentRange compositeIndex = packageSixParam(concentration, compareResult, provinceRangeResult, twentyEightRangeResult, provinceChannelRangeResult, "compositeIndex", 3, "");
|
result.put("PM2_5",PM2_5);
|
result.put("PM10",PM10);
|
result.put("SO2",SO2);
|
result.put("NO2",NO2);
|
result.put("O3",O3);
|
result.put("CO",CO);
|
result.put("compositeIndex",compositeIndex);
|
return result;
|
}
|
|
//打包六参以及综合指数数据对象
|
private DataPercentRange packageSixParam(Map<String, Double> concentration,//对应浓度/天数
|
Map<String, String> compareResult,//同比结果
|
Map<String, Integer> provinceRange,//省内排名
|
Map<String, Integer> twentyEightRange,//2+26排名
|
Map<String, Integer> heBeiEightCitiesRange,//省内通道排名
|
String sensor,//因子
|
int decimal,//保留小数位
|
String unit)//单位
|
{
|
DataPercentRange data = new DataPercentRange();
|
if (decimal == 0)
|
data.setConcentration(concentration.get(sensor).intValue() + " " + unit);
|
else
|
data.setConcentration(AmendUtils.sciCal(concentration.get(sensor), decimal) + " " + unit);
|
if (compareResult != null)
|
data.setPercent(compareResult.get(sensor));
|
if (provinceRange != null)
|
data.setProvinceRange(provinceRange.get(sensor));
|
if (twentyEightRange != null)
|
data.setTwentyEightCitiesRange(twentyEightRange.get(sensor));
|
if (heBeiEightCitiesRange != null)
|
data.setProvinceChannelRange(heBeiEightCitiesRange.get(sensor));
|
return data;
|
}
|
|
//计算六参和综合指数排名
|
private Map<String, Integer> sixParamRange(List<SysArea> cities, SysArea posCity, Date startDate, Date endDate, boolean thisYear) {
|
Map<String, Double> pm2_5Map = new HashMap<>();
|
Map<String, Double> pm10Map = new HashMap<>();
|
Map<String, Double> so2Map = new HashMap<>();
|
Map<String, Double> no2Map = new HashMap<>();
|
Map<String, Double> o3Map = new HashMap<>();
|
Map<String, Double> coMap = new HashMap<>();
|
Map<String, Double> compositeIndexMap = new HashMap<>();
|
|
for (SysArea city : cities) {
|
Map<String, Double> concentration = getConcentration(thisYear, startDate, endDate, city.getAreaCode());
|
if (concentration == null)
|
continue;
|
Object pm2_5o = concentration.get("PM2_5");
|
pm2_5Map.put(city.getAreaCode().toString(), Double.valueOf(pm2_5o.toString()));
|
|
Object pm10o = concentration.get("PM10");
|
pm10Map.put(city.getAreaCode().toString(), Double.valueOf(pm10o.toString()));
|
|
Object so2o = concentration.get("SO2");
|
so2Map.put(city.getAreaCode().toString(), Double.valueOf(so2o.toString()));
|
|
Object no2o = concentration.get("NO2");
|
no2Map.put(city.getAreaCode().toString(), Double.valueOf(no2o.toString()));
|
|
Object o3o = concentration.get("O3");
|
o3Map.put(city.getAreaCode().toString(), Double.valueOf(o3o.toString()));
|
|
Object coo = concentration.get("CO");
|
coMap.put(city.getAreaCode().toString(), Double.valueOf(coo.toString()));
|
|
Object concentrationo = concentration.get("compositeIndex");
|
compositeIndexMap.put(city.getAreaCode().toString(), Double.valueOf(concentrationo.toString()));
|
}
|
Map<String, Integer> result = new HashMap<>();
|
result.put("PM2_5", rangeMap(pm2_5Map, posCity.getAreaCode().toString()));
|
result.put("SO2", rangeMap(so2Map, posCity.getAreaCode().toString()));
|
result.put("NO2", rangeMap(no2Map, posCity.getAreaCode().toString()));
|
result.put("CO", rangeMap(coMap, posCity.getAreaCode().toString()));
|
result.put("O3", rangeMap(o3Map, posCity.getAreaCode().toString()));
|
result.put("PM10", rangeMap(pm10Map, posCity.getAreaCode().toString()));
|
result.put("compositeIndex", rangeMap(compositeIndexMap, posCity.getAreaCode().toString()));
|
return result;
|
}
|
|
//根域map的value进行升序排序,获取排名
|
private Integer rangeMap(Map<String, Double> map, String cityCode) {
|
Set<Map.Entry<String, Double>> entries = map.entrySet();
|
List<Map.Entry<String, Double>> list = new ArrayList<>(entries);
|
list.sort(Comparator.comparing(value -> value.getValue()));
|
for (Map.Entry<String, Double> entry : list) {
|
if (cityCode.equals(entry.getKey()))
|
return list.indexOf(entry) + 1;
|
}
|
return null;
|
}
|
|
|
//计算六参和综合指数同期对比结果
|
private Map<String, String> calculateSixParamYOY(Map<String, Double> dataMap, Map<String, Double> compareDataMap) {
|
Map<String, String> result = new HashMap<>();
|
|
Double pm2_5 = calculateSensorYOY(dataMap, compareDataMap, "PM2_5");
|
if (pm2_5 != null)
|
result.put("PM2_5", pm2_5.intValue() + " μg/m³");
|
|
Double pm10 = calculateSensorYOY(dataMap, compareDataMap, "PM10");
|
if (pm10 != null)
|
result.put("PM10", pm10.intValue() + " μg/m³");
|
|
Double so2 = calculateSensorYOY(dataMap, compareDataMap, "SO2");
|
if (so2 != null)
|
result.put("SO2", so2.intValue() + " μg/m³");
|
|
|
Double no2 = calculateSensorYOY(dataMap, compareDataMap, "NO2");
|
if (no2 != null)
|
result.put("NO2", no2.intValue() + " μg/m³");
|
|
|
Double o3 = calculateSensorYOY(dataMap, compareDataMap, "O3");
|
if (o3 != null)
|
result.put("O3", o3.intValue() + " μg/m³");
|
|
|
Double co = calculateSensorYOY(dataMap, compareDataMap, "CO");
|
if (co != null) {
|
co = AmendUtils.sciCal(co, 1);
|
result.put("CO", co + " mg/m³");
|
}
|
|
//计算综合指数同比数据
|
Object compositeIndexO = dataMap.get("compositeIndex");
|
Object compareCompositeIndexO = compareDataMap.get("compositeIndex");
|
Double compositeIndex = Double.valueOf(compositeIndexO.toString());
|
Double compareCompositeIndex = Double.valueOf(compareCompositeIndexO.toString());
|
if (compositeIndex != null && compareCompositeIndex != null) {
|
Double compareResult = MathUtils.division(compositeIndex - compareCompositeIndex, compareCompositeIndex, 3);
|
compareResult = MathUtils.mul(compareResult, 100);
|
result.put("compositeIndex", compareResult + "%");
|
}
|
if (result.size() != 7)
|
return null;
|
|
return result;
|
}
|
|
//计算单因子同比结果
|
private Double calculateSensorYOY(Map<String, Double> dataMap, Map<String, Double> compareDataMap, String sensor) {
|
Object dataO = dataMap.get(sensor);
|
Double data = Double.valueOf(dataO.toString());
|
|
Object cdataO = compareDataMap.get(sensor);
|
Double compareData = Double.valueOf(cdataO.toString());
|
if (data != null && compareData != null)
|
return data - compareData;
|
return null;
|
}
|
|
//获取城市对应时间的6参以及综合指数均值
|
private Map<String, Double> getConcentration(boolean thisYear, Date startDate, Date endDate, Integer cityCode) {
|
if (thisYear) {
|
List<CityAqiDaily> cityAqiDailies = cityAqiDailyService.getCityAqiDailyByRegionCodeAndTime(cityCode, startDate, endDate);
|
if (ObjectUtils.isEmpty(cityAqiDailies))
|
return null;
|
return calculate6ParamAvg(cityAqiDailies);
|
} else {
|
List<CityAqiYearly> cityAqiYearlies = getCityAqiYearlyByRegionCodeAndTime(cityCode, startDate, endDate);
|
if (ObjectUtils.isEmpty(cityAqiYearlies))
|
return null;
|
CityAqiYearly cityAqiYearly = cityAqiYearlies.get(0);
|
return JSON.parseObject(cityAqiYearly.getValue(), Map.class);
|
}
|
}
|
|
//计算6参以及综合指数平均值
|
private Map<String, Double> calculate6ParamAvg(List<CityAqiDaily> cityAqiList) {
|
Double co = calculatePercent(cityAqiList, "CO", 95);
|
Double pm2_5 = calculateSensorAvg(cityAqiList, "PM2_5");
|
Double pm10 = calculateSensorAvg(cityAqiList, "PM10");
|
Double so2 = calculateSensorAvg(cityAqiList, "SO2");
|
Double no2 = calculateSensorAvg(cityAqiList, "NO2");
|
Double o3 = calculatePercent(cityAqiList, "O3", 90);
|
Double compositeIndex = calculateSensorAvg(cityAqiList, "compositeIndex");
|
Map<String, Double> result = new HashMap<>();
|
result.put("CO", co);
|
result.put("NO2", no2);
|
result.put("SO2", so2);
|
result.put("O3", o3);
|
result.put("PM2_5", pm2_5);
|
result.put("PM10", pm10);
|
result.put("compositeIndex", compositeIndex);
|
return result;
|
}
|
|
//计算因子的平均值
|
private Double calculateSensorAvg(List<CityAqiDaily> cityAqiList, String sensor) {
|
Double sum = 0d;
|
int num = 0;
|
for (CityAqiDaily cityAqi : cityAqiList) {
|
String value = cityAqi.getValue();
|
if (value == null)
|
continue;
|
Map<String, Object> valueMap = JSON.parseObject(value, Map.class);
|
Object sensorValueObject = valueMap.get(sensor);
|
if (sensorValueObject == null)
|
continue;
|
Double sensorValue = Double.valueOf(sensorValueObject.toString());
|
sum = MathUtils.add(sum, sensorValue);
|
num++;
|
}
|
if (num == 0)
|
return null;
|
Double avg = MathUtils.division(sum, num, 2);
|
return avg;
|
}
|
|
//计算百分位
|
private Double calculatePercent(List<CityAqiDaily> cityAqiList, String sensor, int percent) {
|
List<Double> datas = new ArrayList<>();
|
for (CityAqiDaily cityAqi : cityAqiList) {
|
String value = cityAqi.getValue();
|
if (value == null)
|
continue;
|
Map<String, Object> valueMap = JSON.parseObject(value, Map.class);
|
Object sensorValueObject = valueMap.get(sensor);
|
if (sensorValueObject == null)
|
continue;
|
Double sensorValue = Double.valueOf(sensorValueObject.toString());
|
datas.add(sensorValue);
|
}
|
if (datas.size() == 0)
|
return null;
|
datas.sort(Comparator.comparing(value -> value));
|
Double avg = AmendUtils.percentile(datas, percent);
|
return avg;
|
}
|
|
//计算优良天数排名
|
private Integer calculateFineDaysRange(List<SysArea> cities, SysArea posCity, Date startDate, Date endDate) {
|
List<CityPollutionLevel> result = new ArrayList<>();
|
for (SysArea city : cities) {
|
CityPollutionLevel cityPollutionLevel = cityAqiDailyService.calculateDaysByTimeAndSysArea(city, startDate, endDate);
|
result.add(cityPollutionLevel);
|
}
|
//排序
|
result.sort(Comparator.comparing(value -> value.getGoodWeatherDays() + value.getExcellentWeatherDays()));
|
//倒序排名
|
Collections.reverse(result);
|
for (CityPollutionLevel value : result) {
|
if (value.getRegionName().equals(posCity.getAreaName()))
|
return result.indexOf(value);
|
}
|
return null;
|
}
|
|
//判断是否为今年
|
private boolean isThisYear(String year) {
|
return DateUtils.dateToDateString(new Date(), "yyyy").equals(year);
|
}
|
|
|
}
|