| | |
| | | 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.util.Date; |
| | | import java.util.List; |
| | | import java.text.DateFormat; |
| | | import java.util.*; |
| | | |
| | | /** |
| | | * <p> |
| | |
| | | |
| | | @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); |
| | | 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); |
| | | } |
| | | |
| | | |
| | | } |