package com.moral.util;
|
|
import org.springframework.util.ObjectUtils;
|
|
import java.math.BigDecimal;
|
import java.util.ArrayList;
|
import java.util.Collections;
|
import java.util.Date;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
|
import com.alibaba.fastjson.JSONObject;
|
import com.moral.constant.Constants;
|
|
public class AmendUtils {
|
/**
|
* @param value 需要科学计算的数据
|
* @param digit 保留的小数位
|
* @return 功能:四舍六入五成双计算法
|
*/
|
public static double sciCal(double value, int digit) {
|
String result;
|
try {
|
double ratio = Math.pow(10, digit);
|
double _num = value * ratio;
|
double mod = _num % 1;
|
double integer = Math.floor(_num);
|
double returnNum;
|
if (mod > 0.5) {
|
returnNum = (integer + 1) / ratio;
|
} else if (mod < 0.5) {
|
returnNum = integer / ratio;
|
} else {
|
returnNum = (integer % 2 == 0 ? integer : integer + 1) / ratio;
|
}
|
BigDecimal bg = new BigDecimal(returnNum);
|
result = bg.setScale(digit, BigDecimal.ROUND_HALF_UP).toString();
|
} catch (RuntimeException e) {
|
throw e;
|
}
|
return Double.parseDouble(result);
|
}
|
|
/**
|
* @param params 参数,包含数据data,类型type,下限upper,上限lower
|
* @return 功能:臭氧日均值计算
|
*/
|
public static Map<String, Object> getO3AvgOfDay(Map<String, Object> params) {
|
Map<String, Object> result = new HashMap<>();
|
List<Map<String, Object>> list = (List<Map<String, Object>>) params.get("data");
|
Object upper = params.get("o3Upper");
|
Object lower = params.get("o3Lower");
|
double max;
|
List<Double> avgs = new ArrayList<>();
|
for (int i = 7; i <= 23; i++) {
|
List<Double> data = new ArrayList<>();
|
for (Map<String, Object> dataMap : list) {
|
Map<String, Object> dataValue = JSONObject.parseObject((String) dataMap.get("value"), Map.class);
|
Double o3 = Double.parseDouble(dataValue.get(Constants.SENSOR_CODE_O3).toString());
|
|
//O3数据标记位
|
Object flag = dataValue.get(Constants.SENSOR_CODE_O3 + "-" + Constants.MARKER_BIT_KEY);
|
//剔除有效性不足的数据
|
if (!Constants.MARKER_BIT_TRUE.equals(flag)) {
|
continue;
|
}
|
|
//剔除超过上下限的数据
|
if (!ObjectUtils.isEmpty(upper)) {
|
if (o3 > (Double) upper) {
|
continue;
|
}
|
}
|
if (!ObjectUtils.isEmpty(lower)) {
|
if (o3 < (Double) lower) {
|
continue;
|
}
|
}
|
|
int hour = DateUtils.getHour((Date) dataMap.get("time"));
|
if (hour <= i && hour >= i - 7) {
|
data.add(o3);
|
}
|
}
|
if (data.size() < 6) {
|
continue;
|
}
|
double average = data.stream().mapToDouble(aDouble -> aDouble).summaryStatistics().getAverage();
|
avgs.add(average);
|
}
|
max = avgs.stream().mapToDouble(aDouble -> aDouble).summaryStatistics().getMax();
|
if (avgs.size() < 14) {
|
if (max < 160d) {
|
return result;
|
}
|
}
|
result.put(Constants.SENSOR_CODE_O3, sciCal(max, 4));
|
return result;
|
}
|
|
//城市aqi日均值
|
public static Double o3OfDay(List<Map<String, Object>> value) {
|
List<Map<String, Object>> o3_8H = getO3_8H(value);
|
if (!ObjectUtils.isEmpty(o3_8H)) {
|
double o3 = 0d;
|
for (Map<String, Object> o : o3_8H) {
|
double temp = (double) o.get("O3");
|
if (temp > o3) {
|
o3 = temp;
|
}
|
}
|
return sciCal(o3, 0);
|
}
|
return null;
|
}
|
|
/**
|
* @param data 数据 time:Date类型,臭氧key:o3
|
* @return 功能:臭氧8小时滑动值计算
|
*/
|
public static List<Map<String, Object>> getO3_8H(List<Map<String, Object>> data) {
|
Date date = (Date) data.get(0).get("time");
|
List<Map<String, Object>> result = new ArrayList<>();
|
for (int i = 7; i <= 23; i++) {
|
Map<String, Object> map = new HashMap<>();
|
map.put("time", DateUtils.addHours(date, i));
|
List<Double> value = new ArrayList<>();
|
for (Map<String, Object> dataMap : data) {
|
Map<String, Object> sensorValue = JSONObject.parseObject((String) dataMap.get("value"), Map.class);
|
Double o3 = Double.parseDouble(sensorValue.get("O3").toString());
|
Date time = (Date) dataMap.get("time");
|
int hour = DateUtils.getHour(time);
|
if (hour <= i && hour >= i - 7) {
|
value.add(o3);
|
}
|
}
|
if (value.size() < 6) {
|
continue;
|
}
|
double average = value.stream().mapToDouble(aDouble -> aDouble).summaryStatistics().getAverage();
|
map.put("O3", sciCal(average, 0));
|
result.add(map);
|
}
|
return result;
|
}
|
|
/**
|
* @param params 参数
|
* @return 功能:风向均值计算
|
*/
|
public static Map<String, Object> getWindDirAvg(Map<String, Object> params) {
|
Map<String, Object> result = new HashMap<>();
|
List<Map<String, Object>> data = (List<Map<String, Object>>) params.get("data");
|
String type = params.get("type").toString();
|
Object windDirUpper = params.get("windDirUpper");
|
Object windDirLower = params.get("windDirLower");
|
Object windSpeedUpper = params.get("windSpeedUpper");
|
Object windSpeedLower = params.get("windSpeedLower");
|
|
Double avgDir = null;
|
Double sumSin = 0d;
|
Double sumCos = 0d;
|
Integer size = 0;
|
for (Map<String, Object> map : data) {
|
Map<String, Object> dataValue = JSONObject.parseObject((String) map.get("value"), Map.class);
|
Object wind = dataValue.get(Constants.SENSOR_CODE_WIND_DIR);
|
Object speed = dataValue.get(Constants.SENSOR_CODE_WIND_SPEED);
|
Object flagDir = dataValue.get(Constants.SENSOR_CODE_WIND_DIR + "-" + Constants.MARKER_BIT_KEY);
|
Object flagSpeed = dataValue.get(Constants.SENSOR_CODE_WIND_SPEED + "-" + Constants.MARKER_BIT_KEY);
|
|
if (!ObjectUtils.isEmpty(flagDir) && !ObjectUtils.isEmpty(flagSpeed)) {
|
if (!Constants.MARKER_BIT_TRUE.equals(flagDir) || !Constants.MARKER_BIT_TRUE.equals(flagSpeed)) {
|
continue;
|
}
|
}
|
|
if (ObjectUtils.isEmpty(wind) || ObjectUtils.isEmpty(speed)) {
|
continue;
|
}
|
double windDir = Double.parseDouble(wind.toString());
|
double windSpeed = Double.parseDouble(speed.toString());
|
|
//剔除风速和风向超过上下限范围的数据
|
if (!ObjectUtils.isEmpty(windDirUpper)) {
|
if (windDir > (Double) windDirUpper) {
|
continue;
|
}
|
}
|
|
if (!ObjectUtils.isEmpty(windDirLower)) {
|
if (windDir < (Double) windDirLower) {
|
continue;
|
}
|
}
|
|
if (!ObjectUtils.isEmpty(windSpeedUpper)) {
|
if (windSpeed > (Double) windSpeedUpper) {
|
continue;
|
}
|
}
|
|
if (!ObjectUtils.isEmpty(windSpeedLower)) {
|
if (windSpeed < (Double) windSpeedLower) {
|
continue;
|
}
|
}
|
|
size++;
|
double sin = windSpeed * Math.sin(windDir / 180d * Math.PI);
|
double cos = windSpeed * Math.cos(windDir / 180d * Math.PI);
|
sumSin += sin;
|
sumCos += cos;
|
}
|
if (size == 0) {
|
return result;
|
}
|
|
double avgSin = sumSin / size;
|
double avgCos = sumCos / size;
|
|
if (avgSin > 0 && avgCos > 0) {
|
avgDir = Math.atan(avgSin / avgCos) * 180 / Math.PI;
|
} else if ((avgSin > 0 && avgCos < 0) || (avgSin < 0 && avgCos < 0)) {
|
avgDir = Math.atan(avgSin / avgCos) * 180 / Math.PI + 180;
|
} else if (avgSin < 0 && avgCos > 0) {
|
avgDir = Math.atan(avgSin / avgCos) * 180 / Math.PI + 360;
|
}
|
if (!ObjectUtils.isEmpty(avgDir)) {
|
double v = sciCal(avgDir, 4);
|
result.put(Constants.SENSOR_CODE_WIND_DIR, v);
|
if ("hour".equals(type)) {
|
//有效值>=45个,打标记位 N,<45打H H:有效性不足
|
if (size >= 45) {
|
result.put(Constants.SENSOR_CODE_WIND_DIR + "-" + Constants.MARKER_BIT_KEY, Constants.MARKER_BIT_TRUE);
|
} else {
|
result.put(Constants.SENSOR_CODE_WIND_DIR + "-" + Constants.MARKER_BIT_KEY, Constants.MARKER_BIT_FALSE);
|
}
|
}
|
}
|
return result;
|
}
|
|
/**
|
* @param data 需要求百分位的数据
|
* @param p 百分位,例:95百分位,p=95
|
* @return 功能:百分位数计算
|
*/
|
public static double percentile(List<Double> data, int p) {
|
int n = data.size();
|
Collections.sort(data);
|
double v = MathUtils.division(MathUtils.mul(n, p),100d,1);
|
v = Math.ceil(v)-1;
|
return sciCal(data.get((int) v), 4);
|
}
|
|
|
|
//一氧化碳周月均值计算
|
public static Map<String, Object> getCOAvgOfWeekOrMonth(Map<String, Object> params) {
|
Map<String, Object> result = new HashMap<>();
|
List<Map<String, Object>> list = (List<Map<String, Object>>) params.get("data");
|
Object upper = params.get("coUpper");
|
Object lower = params.get("coLower");
|
|
List<Double> data = new ArrayList<>();
|
for (Map<String, Object> dataMap : list) {
|
Map<String, Object> dataValue = JSONObject.parseObject((String) dataMap.get("value"), Map.class);
|
Object o = dataValue.get(Constants.SENSOR_CODE_CO);
|
if (ObjectUtils.isEmpty(o)) {
|
continue;
|
}
|
Double co = Double.parseDouble(o.toString());
|
//剔除超过上下限的数据
|
if (!ObjectUtils.isEmpty(upper)) {
|
if (co > (Double) upper) {
|
continue;
|
}
|
}
|
if (!ObjectUtils.isEmpty(lower)) {
|
if (co < (Double) lower) {
|
continue;
|
}
|
}
|
data.add(co);
|
}
|
if (data.size() == 0) {
|
return result;
|
}
|
result.put(Constants.SENSOR_CODE_CO, percentile(data, 95));
|
return result;
|
}
|
|
//臭氧周月均值计算
|
public static Map<String, Object> getO3AvgOfWeekOrMonth(Map<String, Object> params) {
|
Map<String, Object> result = new HashMap<>();
|
List<Map<String, Object>> list = (List<Map<String, Object>>) params.get("data");
|
Object upper = params.get("o3Upper");
|
Object lower = params.get("o3Lower");
|
List<Double> data = new ArrayList<>();
|
for (Map<String, Object> dataMap : list) {
|
Map<String, Object> dataValue = JSONObject.parseObject((String) dataMap.get("value"), Map.class);
|
Object o = dataValue.get(Constants.SENSOR_CODE_O3);
|
if (ObjectUtils.isEmpty(o)) {
|
continue;
|
}
|
Double o3 = Double.parseDouble(o.toString());
|
//剔除超过上下限的数据
|
if (!ObjectUtils.isEmpty(upper)) {
|
if (o3 > (Double) upper) {
|
continue;
|
}
|
}
|
if (!ObjectUtils.isEmpty(lower)) {
|
if (o3 < (Double) lower) {
|
continue;
|
}
|
}
|
data.add(o3);
|
}
|
if (data.size() == 0) {
|
return result;
|
}
|
result.put(Constants.SENSOR_CODE_O3, percentile(data, 90));
|
return result;
|
}
|
|
//各因子年均值计算
|
/**
|
* 年均值算法
|
* PM2.5,PM10,CO,日均值95百分位
|
* SO2,NO2,日均值98百分位
|
* O3,日最大值90百分位
|
*
|
* @param list 数据
|
* @param sensorCode 因子code
|
*/
|
public static Double getAvgOfYear(List<Map<String, Object>> list, String sensorCode) {
|
List<Double> data = new ArrayList<>();
|
for (Map<String, Object> dataMap : list) {
|
Map<String, Object> dataValue = JSONObject.parseObject((String) dataMap.get("value"), Map.class);
|
Object o = dataValue.get(sensorCode);
|
if (ObjectUtils.isEmpty(o)) {
|
continue;
|
}
|
Double sensorValue = Double.parseDouble(o.toString());
|
data.add(sensorValue);
|
}
|
if (data.size() == 0) {
|
return null;
|
}
|
|
Double v = null;
|
if ("PM2_5".equals(sensorCode) || "PM10".equals(sensorCode) || "CO".equals(sensorCode)) {
|
v = percentile(data, 95);
|
} else if ("SO2".equals(sensorCode) || "NO2".equals(sensorCode)) {
|
v = percentile(data, 98);
|
} else if ("O3".equals(sensorCode)) {
|
v = percentile(data, 90);
|
}
|
return v;
|
}
|
}
|