|
|
@@ -1,786 +0,0 @@
|
|
|
-package easydo.technology.util;
|
|
|
-
|
|
|
-import easydo.technology.model.WeatherData;
|
|
|
-
|
|
|
-import java.time.LocalDateTime;
|
|
|
-import java.util.*;
|
|
|
-import java.util.stream.Collectors;
|
|
|
-
|
|
|
-public class WeatherUtil {
|
|
|
-
|
|
|
- // 天气状态定义
|
|
|
- public enum WeatherState {
|
|
|
- SUNNY, CLOUDY, RAINY, STORMY, FOGGY
|
|
|
- }
|
|
|
-
|
|
|
- // 季节定义
|
|
|
- public enum Season {
|
|
|
- WINTER, SPRING, SUMMER, AUTUMN
|
|
|
- }
|
|
|
-
|
|
|
- // 风向定义(16方位)
|
|
|
- public enum WindDirection {
|
|
|
- N, NNE, NE, ENE, E, ESE, SE, SSE,
|
|
|
- S, SSW, SW, WSW, W, WNW, NW, NNW,
|
|
|
- OTHER
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- // 模拟参数常量
|
|
|
- private static final int TIME_INTERVAL_MINUTES = 3;
|
|
|
-
|
|
|
- // 污染物类型
|
|
|
- private enum PollutantType {
|
|
|
- PM2_5, PM10, PM100, NOISE
|
|
|
- }
|
|
|
-
|
|
|
- // 马尔可夫链状态转移概率矩阵(按季节调整)
|
|
|
- private static final Map<Season, double[][]> TRANSITION_MATRICES;
|
|
|
-
|
|
|
- static {
|
|
|
- Map<Season, double[][]> tempMap = new EnumMap<>(Season.class);
|
|
|
- tempMap.put(Season.WINTER, new double[][]{
|
|
|
- {0.70, 0.20, 0.08, 0.00, 0.02}, // SUNNY
|
|
|
- {0.25, 0.55, 0.15, 0.00, 0.05}, // CLOUDY
|
|
|
- {0.10, 0.30, 0.55, 0.00, 0.05}, // RAINY
|
|
|
- {0.00, 0.00, 0.00, 0.00, 0.00}, // STORMY (冬季无雷暴)
|
|
|
- {0.15, 0.25, 0.10, 0.00, 0.50} // FOGGY
|
|
|
- });
|
|
|
- tempMap.put(Season.SPRING, new double[][]{
|
|
|
- {0.65, 0.25, 0.07, 0.01, 0.02},
|
|
|
- {0.30, 0.50, 0.15, 0.03, 0.02},
|
|
|
- {0.15, 0.35, 0.45, 0.03, 0.02},
|
|
|
- {0.05, 0.15, 0.30, 0.45, 0.05},
|
|
|
- {0.20, 0.30, 0.10, 0.00, 0.40}
|
|
|
- });
|
|
|
- tempMap.put(Season.SUMMER, new double[][]{
|
|
|
- {0.60, 0.30, 0.05, 0.05, 0.00},
|
|
|
- {0.35, 0.45, 0.10, 0.08, 0.02},
|
|
|
- {0.20, 0.40, 0.30, 0.08, 0.02},
|
|
|
- {0.10, 0.20, 0.20, 0.45, 0.05},
|
|
|
- {0.25, 0.35, 0.05, 0.00, 0.35}
|
|
|
- });
|
|
|
- tempMap.put(Season.AUTUMN, new double[][]{
|
|
|
- {0.55, 0.30, 0.10, 0.03, 0.02},
|
|
|
- {0.25, 0.50, 0.20, 0.03, 0.02},
|
|
|
- {0.10, 0.30, 0.55, 0.03, 0.02},
|
|
|
- {0.05, 0.15, 0.30, 0.45, 0.05},
|
|
|
- {0.15, 0.25, 0.10, 0.00, 0.50}
|
|
|
- });
|
|
|
- TRANSITION_MATRICES = Collections.unmodifiableMap(tempMap);
|
|
|
- }
|
|
|
-
|
|
|
- // 基础温度范围(摄氏度,按季节和天气状态)
|
|
|
- private static final Map<Season, Map<WeatherState, int[]>> BASE_TEMPERATURES;
|
|
|
-
|
|
|
- static {
|
|
|
- Map<Season, Map<WeatherState, int[]>> tempMap = new EnumMap<>(Season.class);
|
|
|
-
|
|
|
- Map<WeatherState, int[]> winterTemps = new EnumMap<>(WeatherState.class);
|
|
|
- winterTemps.put(WeatherState.SUNNY, new int[]{-5, 5});
|
|
|
- winterTemps.put(WeatherState.CLOUDY, new int[]{-8, 2});
|
|
|
- winterTemps.put(WeatherState.RAINY, new int[]{-3, 3});
|
|
|
- winterTemps.put(WeatherState.STORMY, new int[]{-5, 0});
|
|
|
- winterTemps.put(WeatherState.FOGGY, new int[]{-10, 0});
|
|
|
- tempMap.put(Season.WINTER, Collections.unmodifiableMap(winterTemps));
|
|
|
-
|
|
|
- Map<WeatherState, int[]> springTemps = new EnumMap<>(WeatherState.class);
|
|
|
- springTemps.put(WeatherState.SUNNY, new int[]{8, 18});
|
|
|
- springTemps.put(WeatherState.CLOUDY, new int[]{5, 15});
|
|
|
- springTemps.put(WeatherState.RAINY, new int[]{6, 12});
|
|
|
- springTemps.put(WeatherState.STORMY, new int[]{10, 16});
|
|
|
- springTemps.put(WeatherState.FOGGY, new int[]{3, 10});
|
|
|
- tempMap.put(Season.SPRING, Collections.unmodifiableMap(springTemps));
|
|
|
-
|
|
|
- Map<WeatherState, int[]> summerTemps = new EnumMap<>(WeatherState.class);
|
|
|
- summerTemps.put(WeatherState.SUNNY, new int[]{20, 32});
|
|
|
- summerTemps.put(WeatherState.CLOUDY, new int[]{18, 28});
|
|
|
- summerTemps.put(WeatherState.RAINY, new int[]{16, 24});
|
|
|
- summerTemps.put(WeatherState.STORMY, new int[]{22, 28});
|
|
|
- summerTemps.put(WeatherState.FOGGY, new int[]{15, 22});
|
|
|
- tempMap.put(Season.SUMMER, Collections.unmodifiableMap(summerTemps));
|
|
|
-
|
|
|
- Map<WeatherState, int[]> autumnTemps = new EnumMap<>(WeatherState.class);
|
|
|
- autumnTemps.put(WeatherState.SUNNY, new int[]{15, 25});
|
|
|
- autumnTemps.put(WeatherState.CLOUDY, new int[]{13, 22});
|
|
|
- autumnTemps.put(WeatherState.RAINY, new int[]{10, 20});
|
|
|
- autumnTemps.put(WeatherState.STORMY, new int[]{13, 24});
|
|
|
- autumnTemps.put(WeatherState.FOGGY, new int[]{10, 18});
|
|
|
- tempMap.put(Season.AUTUMN, Collections.unmodifiableMap(autumnTemps));
|
|
|
-
|
|
|
- BASE_TEMPERATURES = Collections.unmodifiableMap(tempMap);
|
|
|
- }
|
|
|
-
|
|
|
- // 基础湿度范围(百分比,按天气状态)
|
|
|
- private static final Map<WeatherState, int[]> BASE_HUMIDITIES;
|
|
|
-
|
|
|
- static {
|
|
|
- Map<WeatherState, int[]> tempMap = new EnumMap<>(WeatherState.class);
|
|
|
- tempMap.put(WeatherState.SUNNY, new int[]{30, 60});
|
|
|
- tempMap.put(WeatherState.CLOUDY, new int[]{50, 80});
|
|
|
- tempMap.put(WeatherState.RAINY, new int[]{75, 90});
|
|
|
- tempMap.put(WeatherState.STORMY, new int[]{70, 88});
|
|
|
- tempMap.put(WeatherState.FOGGY, new int[]{70, 85});
|
|
|
- BASE_HUMIDITIES = Collections.unmodifiableMap(tempMap);
|
|
|
- }
|
|
|
-
|
|
|
- // 新增:基础污染物范围(按天气状态和季节)
|
|
|
- private static final Map<Season, Map<WeatherState, Map<PollutantType, int[]>>> BASE_POLLUTANTS;
|
|
|
-
|
|
|
- static {
|
|
|
- Map<Season, Map<WeatherState, Map<PollutantType, int[]>>> tempMap = new EnumMap<>(Season.class);
|
|
|
-
|
|
|
- // 冬季污染物范围
|
|
|
- Map<WeatherState, Map<PollutantType, int[]>> winterPollutants = new EnumMap<>(WeatherState.class);
|
|
|
- winterPollutants.put(WeatherState.SUNNY, createPollutantMap(25, 75, 30, 80, 40, 90, 45, 65));
|
|
|
- winterPollutants.put(WeatherState.CLOUDY, createPollutantMap(20, 60, 25, 70, 35, 90, 40, 60));
|
|
|
- winterPollutants.put(WeatherState.RAINY, createPollutantMap(5, 20, 10, 30, 20, 50, 35, 55));
|
|
|
- winterPollutants.put(WeatherState.STORMY, createPollutantMap(10, 30, 15, 40, 25, 60, 50, 70));
|
|
|
- winterPollutants.put(WeatherState.FOGGY, createPollutantMap(70, 90, 80, 100, 90, 120, 40, 60));
|
|
|
- tempMap.put(Season.WINTER, Collections.unmodifiableMap(winterPollutants));
|
|
|
-
|
|
|
- // 春季污染物范围
|
|
|
- Map<WeatherState, Map<PollutantType, int[]>> springPollutants = new EnumMap<>(WeatherState.class);
|
|
|
- springPollutants.put(WeatherState.SUNNY, createPollutantMap(15, 40, 20, 50, 30, 70, 50, 70));
|
|
|
- springPollutants.put(WeatherState.CLOUDY, createPollutantMap(20, 50, 25, 60, 35, 80, 45, 65));
|
|
|
- springPollutants.put(WeatherState.RAINY, createPollutantMap(5, 25, 10, 35, 20, 60, 40, 60));
|
|
|
- springPollutants.put(WeatherState.STORMY, createPollutantMap(10, 35, 15, 45, 25, 70, 55, 75));
|
|
|
- springPollutants.put(WeatherState.FOGGY, createPollutantMap(40, 80, 60, 100, 80, 110, 45, 65));
|
|
|
- tempMap.put(Season.SPRING, Collections.unmodifiableMap(springPollutants));
|
|
|
-
|
|
|
- // 夏季污染物范围
|
|
|
- Map<WeatherState, Map<PollutantType, int[]>> summerPollutants = new EnumMap<>(WeatherState.class);
|
|
|
- summerPollutants.put(WeatherState.SUNNY, createPollutantMap(10, 30, 15, 40, 25, 60, 55, 75));
|
|
|
- summerPollutants.put(WeatherState.CLOUDY, createPollutantMap(15, 35, 20, 45, 30, 70, 50, 70));
|
|
|
- summerPollutants.put(WeatherState.RAINY, createPollutantMap(5, 20, 10, 30, 20, 50, 45, 65));
|
|
|
- summerPollutants.put(WeatherState.STORMY, createPollutantMap(8, 25, 12, 35, 22, 55, 60, 80));
|
|
|
- summerPollutants.put(WeatherState.FOGGY, createPollutantMap(30, 80, 50, 100, 70, 90, 50, 70));
|
|
|
- tempMap.put(Season.SUMMER, Collections.unmodifiableMap(summerPollutants));
|
|
|
-
|
|
|
- // 秋季污染物范围
|
|
|
- Map<WeatherState, Map<PollutantType, int[]>> autumnPollutants = new EnumMap<>(WeatherState.class);
|
|
|
- autumnPollutants.put(WeatherState.SUNNY, createPollutantMap(20, 50, 25, 60, 35, 80, 50, 70));
|
|
|
- autumnPollutants.put(WeatherState.CLOUDY, createPollutantMap(25, 60, 30, 70, 40, 90, 45, 65));
|
|
|
- autumnPollutants.put(WeatherState.RAINY, createPollutantMap(8, 30, 12, 40, 25, 70, 40, 60));
|
|
|
- autumnPollutants.put(WeatherState.STORMY, createPollutantMap(12, 40, 18, 50, 30, 80, 55, 75));
|
|
|
- autumnPollutants.put(WeatherState.FOGGY, createPollutantMap(60, 80, 80, 100, 80, 110, 45, 65));
|
|
|
- tempMap.put(Season.AUTUMN, Collections.unmodifiableMap(autumnPollutants));
|
|
|
-
|
|
|
- BASE_POLLUTANTS = Collections.unmodifiableMap(tempMap);
|
|
|
- }
|
|
|
-
|
|
|
- // 新增:基础风速范围(米/秒,按季节和天气状态)
|
|
|
- private static final Map<Season, Map<WeatherState, int[]>> BASE_WIND_SPEEDS;
|
|
|
-
|
|
|
- static {
|
|
|
- Map<Season, Map<WeatherState, int[]>> tempMap = new EnumMap<>(Season.class);
|
|
|
-
|
|
|
- Map<WeatherState, int[]> winterSpeeds = new EnumMap<>(WeatherState.class);
|
|
|
- winterSpeeds.put(WeatherState.SUNNY, new int[]{1, 5});
|
|
|
- winterSpeeds.put(WeatherState.CLOUDY, new int[]{2, 8});
|
|
|
- winterSpeeds.put(WeatherState.RAINY, new int[]{3, 10});
|
|
|
- winterSpeeds.put(WeatherState.STORMY, new int[]{6, 10});
|
|
|
-// winterSpeeds.put(WeatherState.STORMY, new int[]{10, 20});
|
|
|
- winterSpeeds.put(WeatherState.FOGGY, new int[]{0, 3});
|
|
|
- tempMap.put(Season.WINTER, Collections.unmodifiableMap(winterSpeeds));
|
|
|
-
|
|
|
- Map<WeatherState, int[]> springSpeeds = new EnumMap<>(WeatherState.class);
|
|
|
- springSpeeds.put(WeatherState.SUNNY, new int[]{2, 6});
|
|
|
- springSpeeds.put(WeatherState.CLOUDY, new int[]{3, 8});
|
|
|
-// springSpeeds.put(WeatherState.RAINY, new int[]{4, 12});
|
|
|
- springSpeeds.put(WeatherState.RAINY, new int[]{4, 9});
|
|
|
-// springSpeeds.put(WeatherState.STORMY, new int[]{12, 25});
|
|
|
- springSpeeds.put(WeatherState.STORMY, new int[]{6, 10});
|
|
|
- springSpeeds.put(WeatherState.FOGGY, new int[]{1, 4});
|
|
|
- tempMap.put(Season.SPRING, Collections.unmodifiableMap(springSpeeds));
|
|
|
-
|
|
|
- Map<WeatherState, int[]> summerSpeeds = new EnumMap<>(WeatherState.class);
|
|
|
- summerSpeeds.put(WeatherState.SUNNY, new int[]{1, 4});
|
|
|
- summerSpeeds.put(WeatherState.CLOUDY, new int[]{2, 6});
|
|
|
- summerSpeeds.put(WeatherState.RAINY, new int[]{3, 9});
|
|
|
-// summerSpeeds.put(WeatherState.STORMY, new int[]{15, 30});
|
|
|
- summerSpeeds.put(WeatherState.STORMY, new int[]{6, 10});
|
|
|
- summerSpeeds.put(WeatherState.FOGGY, new int[]{0, 2});
|
|
|
- tempMap.put(Season.SUMMER, Collections.unmodifiableMap(summerSpeeds));
|
|
|
-
|
|
|
- Map<WeatherState, int[]> autumnSpeeds = new EnumMap<>(WeatherState.class);
|
|
|
- autumnSpeeds.put(WeatherState.SUNNY, new int[]{2, 7});
|
|
|
-// autumnSpeeds.put(WeatherState.CLOUDY, new int[]{3, 10});
|
|
|
- autumnSpeeds.put(WeatherState.CLOUDY, new int[]{3, 7});
|
|
|
-// autumnSpeeds.put(WeatherState.RAINY, new int[]{4, 14});
|
|
|
- autumnSpeeds.put(WeatherState.RAINY, new int[]{4, 9});
|
|
|
-// autumnSpeeds.put(WeatherState.STORMY, new int[]{10, 25});
|
|
|
- autumnSpeeds.put(WeatherState.STORMY, new int[]{6, 10});
|
|
|
- autumnSpeeds.put(WeatherState.FOGGY, new int[]{1, 5});
|
|
|
- tempMap.put(Season.AUTUMN, Collections.unmodifiableMap(autumnSpeeds));
|
|
|
-
|
|
|
- BASE_WIND_SPEEDS = Collections.unmodifiableMap(tempMap);
|
|
|
- }
|
|
|
-
|
|
|
- // 新增:主导风向概率(按季节)
|
|
|
- private static final Map<Season, Map<WindDirection, Double>> DOMINANT_WIND_DIRECTIONS;
|
|
|
-
|
|
|
- static {
|
|
|
- Map<Season, Map<WindDirection, Double>> tempMap = new EnumMap<>(Season.class);
|
|
|
-
|
|
|
- // 冬季主导风向(北风、西北风)
|
|
|
- Map<WindDirection, Double> winterDirs = new EnumMap<>(WindDirection.class);
|
|
|
- winterDirs.put(WindDirection.N, 0.35);
|
|
|
- winterDirs.put(WindDirection.NNW, 0.20);
|
|
|
- winterDirs.put(WindDirection.NW, 0.15);
|
|
|
- winterDirs.put(WindDirection.NNE, 0.10);
|
|
|
- winterDirs.put(WindDirection.W, 0.05);
|
|
|
- winterDirs.put(WindDirection.E, 0.05);
|
|
|
- winterDirs.put(WindDirection.S, 0.05);
|
|
|
- winterDirs.put(WindDirection.OTHER, 0.05);
|
|
|
- tempMap.put(Season.WINTER, Collections.unmodifiableMap(winterDirs));
|
|
|
-
|
|
|
- // 春季主导风向(东南风、南风)
|
|
|
- Map<WindDirection, Double> springDirs = new EnumMap<>(WindDirection.class);
|
|
|
- springDirs.put(WindDirection.SE, 0.30);
|
|
|
- springDirs.put(WindDirection.S, 0.25);
|
|
|
- springDirs.put(WindDirection.SSE, 0.15);
|
|
|
- springDirs.put(WindDirection.ESE, 0.10);
|
|
|
- springDirs.put(WindDirection.E, 0.05);
|
|
|
- springDirs.put(WindDirection.N, 0.05);
|
|
|
- springDirs.put(WindDirection.W, 0.05);
|
|
|
- springDirs.put(WindDirection.OTHER, 0.05);
|
|
|
- tempMap.put(Season.SPRING, Collections.unmodifiableMap(springDirs));
|
|
|
-
|
|
|
- // 夏季主导风向(南风、西南风)
|
|
|
- Map<WindDirection, Double> summerDirs = new EnumMap<>(WindDirection.class);
|
|
|
- summerDirs.put(WindDirection.S, 0.30);
|
|
|
- summerDirs.put(WindDirection.SSW, 0.20);
|
|
|
- summerDirs.put(WindDirection.SW, 0.15);
|
|
|
- summerDirs.put(WindDirection.SSE, 0.10);
|
|
|
- summerDirs.put(WindDirection.W, 0.05);
|
|
|
- summerDirs.put(WindDirection.N, 0.05);
|
|
|
- summerDirs.put(WindDirection.E, 0.05);
|
|
|
- summerDirs.put(WindDirection.OTHER, 0.10);
|
|
|
- tempMap.put(Season.SUMMER, Collections.unmodifiableMap(summerDirs));
|
|
|
-
|
|
|
- // 秋季主导风向(西北风、西风)
|
|
|
- Map<WindDirection, Double> autumnDirs = new EnumMap<>(WindDirection.class);
|
|
|
- autumnDirs.put(WindDirection.NW, 0.30);
|
|
|
- autumnDirs.put(WindDirection.W, 0.25);
|
|
|
- autumnDirs.put(WindDirection.WNW, 0.15);
|
|
|
- autumnDirs.put(WindDirection.NNW, 0.10);
|
|
|
- autumnDirs.put(WindDirection.N, 0.05);
|
|
|
- autumnDirs.put(WindDirection.S, 0.05);
|
|
|
- autumnDirs.put(WindDirection.E, 0.05);
|
|
|
- autumnDirs.put(WindDirection.OTHER, 0.05);
|
|
|
- tempMap.put(Season.AUTUMN, Collections.unmodifiableMap(autumnDirs));
|
|
|
-
|
|
|
- DOMINANT_WIND_DIRECTIONS = Collections.unmodifiableMap(tempMap);
|
|
|
- }
|
|
|
-
|
|
|
- // 辅助方法:创建污染物范围映射
|
|
|
- private static Map<PollutantType, int[]> createPollutantMap(int pm25Min, int pm25Max,
|
|
|
- int pm10Min, int pm10Max,
|
|
|
- int pm100Min, int pm100Max,
|
|
|
- int noiseMin, int noiseMax) {
|
|
|
- Map<PollutantType, int[]> map = new EnumMap<>(PollutantType.class);
|
|
|
- map.put(PollutantType.PM2_5, new int[]{pm25Min, pm25Max});
|
|
|
- map.put(PollutantType.PM10, new int[]{pm10Min, pm10Max});
|
|
|
- map.put(PollutantType.PM100, new int[]{pm100Min, pm100Max});
|
|
|
- map.put(PollutantType.NOISE, new int[]{noiseMin, noiseMax});
|
|
|
- return Collections.unmodifiableMap(map);
|
|
|
- }
|
|
|
-
|
|
|
- // 辅助方法:获取随机风向(考虑季节主导风向)
|
|
|
- private static WindDirection getRandomWindDirection(Season season, Random random) {
|
|
|
- Map<WindDirection, Double> probs = DOMINANT_WIND_DIRECTIONS.get(season);
|
|
|
- double rand = random.nextDouble();
|
|
|
- double cumulative = 0.0;
|
|
|
-
|
|
|
- for (Map.Entry<WindDirection, Double> entry : probs.entrySet()) {
|
|
|
- cumulative += entry.getValue();
|
|
|
- if (rand < cumulative) {
|
|
|
- if (entry.getKey() == WindDirection.OTHER) {
|
|
|
- while (true){
|
|
|
- List<WindDirection> allDirs = Arrays.asList(WindDirection.values());
|
|
|
- WindDirection direction = allDirs.get(random.nextInt(allDirs.size()));
|
|
|
- if(!direction.equals(WindDirection.OTHER)){
|
|
|
- return direction;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- return entry.getKey();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 默认返回北风
|
|
|
- return WindDirection.N;
|
|
|
- }
|
|
|
-
|
|
|
- // 辅助方法:获取风向角度(0-360度)
|
|
|
- private static int getDirectionAngle(WindDirection direction) {
|
|
|
- return direction.ordinal() * 22; // 每个方位22.5度,近似为22度
|
|
|
- }
|
|
|
-
|
|
|
- // 辅助方法:计算两个风向之间的角度差(考虑圆形)
|
|
|
- private static int getAngleDifference(int angle1, int angle2) {
|
|
|
- int diff = Math.abs(angle1 - angle2);
|
|
|
- return Math.min(diff, 360 - diff);
|
|
|
- }
|
|
|
-
|
|
|
- public static List<WeatherData> simulateYearlyWeather(LocalDateTime currentTime, LocalDateTime endTime) {
|
|
|
- List<WeatherData> yearlyData = new ArrayList<>();
|
|
|
- Random random = new Random();
|
|
|
-
|
|
|
-// LocalDateTime currentTime = LocalDateTime.of(year, 1, 1, 0, 0);
|
|
|
-// LocalDateTime endTime = LocalDateTime.of(year + 1, 1, 1, 0, 0);
|
|
|
-
|
|
|
- // 初始状态
|
|
|
- WeatherState currentState = WeatherState.CLOUDY;
|
|
|
- Season currentSeason = getSeason(currentTime);
|
|
|
-
|
|
|
- // 使用平均值初始化
|
|
|
- double currentTemp = getAverageTemperature(currentSeason, currentState);
|
|
|
- double currentHumidity = getAverageHumidity(currentState);
|
|
|
- double currentPM25 = getAveragePollutant(currentSeason, currentState, PollutantType.PM2_5);
|
|
|
- double currentPM10 = getAveragePollutant(currentSeason, currentState, PollutantType.PM10);
|
|
|
- double currentPM100 = getAveragePollutant(currentSeason, currentState, PollutantType.PM100);
|
|
|
- double currentNoise = getAveragePollutant(currentSeason, currentState, PollutantType.NOISE);
|
|
|
- double currentWindSpeed = getAverageWindSpeed(currentSeason, currentState);
|
|
|
- WindDirection currentWindDir = getRandomWindDirection(currentSeason, random);
|
|
|
-
|
|
|
- // 状态切换时的过渡参数
|
|
|
- WeatherState targetState = currentState;
|
|
|
- int transitionSteps = 0;
|
|
|
- final int TOTAL_TRANSITION_STEPS = 12; // 过渡时长 = 12*3min = 36分钟
|
|
|
-
|
|
|
- while (currentTime.isBefore(endTime)) {
|
|
|
- // 每8小时检查状态变化
|
|
|
- if (currentTime.getMinute() == 0 && currentTime.getHour() % 8 == 0) {
|
|
|
- currentSeason = getSeason(currentTime);
|
|
|
- WeatherState newState = getNextWeatherState(currentState, currentSeason, random);
|
|
|
-
|
|
|
- if (!newState.equals(currentState)) {
|
|
|
- targetState = newState;
|
|
|
- transitionSteps = 0;
|
|
|
-// System.out.println("开始状态过渡: " + currentState + " → " + targetState + " at " + currentTime);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 计算过渡比例 (0.0=当前状态 → 1.0=目标状态)
|
|
|
- double transitionRatio = transitionSteps < TOTAL_TRANSITION_STEPS ?
|
|
|
- (double) transitionSteps / TOTAL_TRANSITION_STEPS : 1.0;
|
|
|
-
|
|
|
- // 获取混合状态下的参数范围
|
|
|
- int[] tempRange = getTransitionTemperatureRange(currentSeason, currentState, targetState, transitionRatio);
|
|
|
- int[] humidityRange = getTransitionHumidityRange(currentState, targetState, transitionRatio);
|
|
|
- Map<PollutantType, int[]> pollutantRanges = getTransitionPollutantRanges(
|
|
|
- currentSeason, currentState, targetState, transitionRatio);
|
|
|
- int[] windSpeedRange = getTransitionWindSpeedRange(currentSeason, currentState, targetState, transitionRatio);
|
|
|
-
|
|
|
- // 获取目标风向(只在过渡开始时确定)
|
|
|
- WindDirection targetWindDir = transitionSteps == 0 ?
|
|
|
- getRandomWindDirection(currentSeason, random) : currentWindDir;
|
|
|
-
|
|
|
- // 生成数据
|
|
|
- double timeFactor = calculateTimeFactor(currentTime);
|
|
|
- double seasonProgress = getSeasonProgress(currentTime);
|
|
|
-
|
|
|
- currentTemp = generateSmoothTemperature(
|
|
|
- tempRange, currentTime, timeFactor, seasonProgress,
|
|
|
- random, currentTemp, transitionRatio);
|
|
|
-
|
|
|
- currentHumidity = generateSmoothHumidity(
|
|
|
- humidityRange, currentTime, timeFactor,
|
|
|
- random, currentHumidity, transitionRatio);
|
|
|
-
|
|
|
- // 生成污染物数据
|
|
|
- currentPM25 = generateSmoothPollutant(
|
|
|
- pollutantRanges.get(PollutantType.PM2_5),
|
|
|
- random, currentPM25, transitionRatio, 1.5, 0.3);
|
|
|
-
|
|
|
- currentPM10 = generateSmoothPollutant(
|
|
|
- pollutantRanges.get(PollutantType.PM10),
|
|
|
- random, currentPM10, transitionRatio, 2.0, 0.4);
|
|
|
-
|
|
|
- currentPM100 = generateSmoothPollutant(
|
|
|
- pollutantRanges.get(PollutantType.PM100),
|
|
|
- random, currentPM100, transitionRatio, 3.0, 0.5);
|
|
|
-
|
|
|
- // 生成噪音
|
|
|
- currentNoise = generateSmoothNoise(
|
|
|
- pollutantRanges.get(PollutantType.NOISE),
|
|
|
- currentTime, random, currentNoise, transitionRatio);
|
|
|
-
|
|
|
- // 生成风速
|
|
|
- currentWindSpeed = generateSmoothWindSpeed(
|
|
|
- windSpeedRange, random, currentWindSpeed, transitionRatio);
|
|
|
-
|
|
|
- // 生成风向(平滑过渡)
|
|
|
- currentWindDir = generateSmoothWindDirection(
|
|
|
- currentWindDir, targetWindDir, transitionRatio, random);
|
|
|
-
|
|
|
- // 添加到结果集
|
|
|
- yearlyData.add(new WeatherData(
|
|
|
- currentTime,
|
|
|
- currentTemp,
|
|
|
- currentHumidity,
|
|
|
- transitionRatio < 1.0 ? currentState : targetState, // 显示当前有效状态
|
|
|
- currentPM25,
|
|
|
- currentPM10,
|
|
|
- currentPM100,
|
|
|
- currentNoise,
|
|
|
- currentWindSpeed,
|
|
|
- currentWindDir
|
|
|
- ));
|
|
|
-
|
|
|
- // 更新状态过渡进度
|
|
|
- if (transitionSteps < TOTAL_TRANSITION_STEPS) {
|
|
|
- transitionSteps++;
|
|
|
- if (transitionSteps == TOTAL_TRANSITION_STEPS) {
|
|
|
- currentState = targetState; // 过渡完成
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- currentTime = currentTime.plusMinutes(TIME_INTERVAL_MINUTES);
|
|
|
- }
|
|
|
-
|
|
|
- return yearlyData;
|
|
|
- }
|
|
|
-
|
|
|
- // 获取过渡期间的温度范围
|
|
|
- private static int[] getTransitionTemperatureRange(Season season,
|
|
|
- WeatherState from,
|
|
|
- WeatherState to,
|
|
|
- double ratio) {
|
|
|
- int[] fromRange = BASE_TEMPERATURES.get(season).get(from);
|
|
|
- int[] toRange = BASE_TEMPERATURES.get(season).get(to);
|
|
|
-
|
|
|
- return new int[]{
|
|
|
- (int) Math.round(fromRange[0] * (1 - ratio) + toRange[0] * ratio),
|
|
|
- (int) Math.round(fromRange[1] * (1 - ratio) + toRange[1] * ratio)
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- // 获取过渡期间的湿度范围
|
|
|
- private static int[] getTransitionHumidityRange(WeatherState from,
|
|
|
- WeatherState to,
|
|
|
- double ratio) {
|
|
|
- int[] fromRange = BASE_HUMIDITIES.get(from);
|
|
|
- int[] toRange = BASE_HUMIDITIES.get(to);
|
|
|
-
|
|
|
- return new int[]{
|
|
|
- (int) Math.round(fromRange[0] * (1 - ratio) + toRange[0] * ratio),
|
|
|
- (int) Math.round(fromRange[1] * (1 - ratio) + toRange[1] * ratio)
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- // 新增:获取过渡期间的污染物范围
|
|
|
- private static Map<PollutantType, int[]> getTransitionPollutantRanges(Season season,
|
|
|
- WeatherState from,
|
|
|
- WeatherState to,
|
|
|
- double ratio) {
|
|
|
- Map<PollutantType, int[]> fromRanges = BASE_POLLUTANTS.get(season).get(from);
|
|
|
- Map<PollutantType, int[]> toRanges = BASE_POLLUTANTS.get(season).get(to);
|
|
|
-
|
|
|
- Map<PollutantType, int[]> result = new EnumMap<>(PollutantType.class);
|
|
|
-
|
|
|
- for (PollutantType type : PollutantType.values()) {
|
|
|
- int[] fromRange = fromRanges.get(type);
|
|
|
- int[] toRange = toRanges.get(type);
|
|
|
-
|
|
|
- int min = (int) Math.round(fromRange[0] * (1 - ratio) + toRange[0] * ratio);
|
|
|
- int max = (int) Math.round(fromRange[1] * (1 - ratio) + toRange[1] * ratio);
|
|
|
- result.put(type, new int[]{min, max});
|
|
|
- }
|
|
|
-
|
|
|
- return Collections.unmodifiableMap(result);
|
|
|
- }
|
|
|
-
|
|
|
- // 新增:获取过渡期间的风速范围
|
|
|
- private static int[] getTransitionWindSpeedRange(Season season,
|
|
|
- WeatherState from,
|
|
|
- WeatherState to,
|
|
|
- double ratio) {
|
|
|
- int[] fromRange = BASE_WIND_SPEEDS.get(season).get(from);
|
|
|
- int[] toRange = BASE_WIND_SPEEDS.get(season).get(to);
|
|
|
-
|
|
|
- return new int[]{
|
|
|
- (int) Math.round(fromRange[0] * (1 - ratio) + toRange[0] * ratio),
|
|
|
- (int) Math.round(fromRange[1] * (1 - ratio) + toRange[1] * ratio)
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- // 辅助方法:获取平均温度
|
|
|
- private static double getAverageTemperature(Season season, WeatherState state) {
|
|
|
- int[] range = BASE_TEMPERATURES.get(season).get(state);
|
|
|
- return (range[0] + range[1]) / 2.0;
|
|
|
- }
|
|
|
-
|
|
|
- // 辅助方法:获取平均湿度
|
|
|
- private static double getAverageHumidity(WeatherState state) {
|
|
|
- int[] range = BASE_HUMIDITIES.get(state);
|
|
|
- return (range[0] + range[1]) / 2.0;
|
|
|
- }
|
|
|
-
|
|
|
- // 辅助方法:获取平均污染物浓度
|
|
|
- private static double getAveragePollutant(Season season, WeatherState state, PollutantType type) {
|
|
|
- int[] range = BASE_POLLUTANTS.get(season).get(state).get(type);
|
|
|
- return (range[0] + range[1]) / 2.0;
|
|
|
- }
|
|
|
-
|
|
|
- // 辅助方法:获取平均风速
|
|
|
- private static double getAverageWindSpeed(Season season, WeatherState state) {
|
|
|
- int[] range = BASE_WIND_SPEEDS.get(season).get(state);
|
|
|
- return (range[0] + range[1]) / 2.0;
|
|
|
- }
|
|
|
-
|
|
|
- // 使用马尔可夫链获取下一个天气状态
|
|
|
- private static WeatherState getNextWeatherState(WeatherState currentState, Season season, Random random) {
|
|
|
- double[][] matrix = TRANSITION_MATRICES.get(season);
|
|
|
- double rand = random.nextDouble();
|
|
|
- double cumulativeProb = 0.0;
|
|
|
-
|
|
|
- for (int i = 0; i < matrix[currentState.ordinal()].length; i++) {
|
|
|
- cumulativeProb += matrix[currentState.ordinal()][i];
|
|
|
- if (rand < cumulativeProb) {
|
|
|
- return WeatherState.values()[i];
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return currentState;
|
|
|
- }
|
|
|
-
|
|
|
- // 计算时间影响因子(昼夜节律)
|
|
|
- private static double calculateTimeFactor(LocalDateTime time) {
|
|
|
- double hour = time.getHour() + time.getMinute() / 60.0;
|
|
|
- // 使用两个正弦波叠加模拟昼夜温度变化
|
|
|
- return 0.6 * Math.sin((hour / 24) * 2 * Math.PI - Math.PI / 2) +
|
|
|
- 0.4 * Math.sin((hour / 12) * 2 * Math.PI);
|
|
|
- }
|
|
|
-
|
|
|
- // 获取季节进度(0.0到1.0)
|
|
|
- private static double getSeasonProgress(LocalDateTime time) {
|
|
|
- int dayOfYear = time.getDayOfYear();
|
|
|
- if (dayOfYear < 80) return dayOfYear / 80.0; // 冬季
|
|
|
- else if (dayOfYear < 172) return (dayOfYear - 80) / 92.0; // 春季
|
|
|
- else if (dayOfYear < 264) return (dayOfYear - 172) / 92.0;// 夏季
|
|
|
- else if (dayOfYear < 355) return (dayOfYear - 264) / 91.0;// 秋季
|
|
|
- else return (dayOfYear - 355) / 10.0; // 冬季
|
|
|
- }
|
|
|
-
|
|
|
- // 平滑温度生成(过渡感知)
|
|
|
- private static double generateSmoothTemperature(int[] range, LocalDateTime time,
|
|
|
- double timeFactor, double seasonProgress,
|
|
|
- Random random, double previousTemp,
|
|
|
- double transitionRatio) {
|
|
|
- // 基础温度计算(考虑过渡比例)
|
|
|
- double baseTemp = range[0] + (range[1] - range[0]) * seasonProgress;
|
|
|
-
|
|
|
- // 昼夜影响(过渡期间减弱)
|
|
|
- double diurnalEffect = timeFactor * (range[1] - range[0]) *
|
|
|
- (0.25 * (1 - transitionRatio) + 0.15 * transitionRatio);
|
|
|
-
|
|
|
- // 随机波动(过渡期间减小)
|
|
|
- double randomEffect = (random.nextDouble() - 0.5) *
|
|
|
- (1.0 * (1 - transitionRatio) + 0.3 * transitionRatio);
|
|
|
-
|
|
|
- // 计算目标温度
|
|
|
- double targetTemp = baseTemp + diurnalEffect + randomEffect;
|
|
|
-
|
|
|
- // 应用变化率限制(过渡期间更严格)
|
|
|
- double maxDelta = 0.3 + 0.2 * (1 - transitionRatio); // 0.3~0.5℃
|
|
|
- targetTemp = Math.max(previousTemp - maxDelta,
|
|
|
- Math.min(previousTemp + maxDelta, targetTemp));
|
|
|
-
|
|
|
- // 确保在合理范围内
|
|
|
- return Math.max(range[0], Math.min(range[1], targetTemp));
|
|
|
- }
|
|
|
-
|
|
|
- // 平滑湿度生成(过渡感知)
|
|
|
- private static double generateSmoothHumidity(int[] range, LocalDateTime time,
|
|
|
- double timeFactor, Random random,
|
|
|
- double previousHumidity,
|
|
|
- double transitionRatio) {
|
|
|
- // 基础湿度(考虑过渡比例)
|
|
|
- double baseHumidity = range[0] + (range[1] - range[0]) * random.nextDouble();
|
|
|
-
|
|
|
- // 昼夜影响(过渡期间减弱)
|
|
|
- double diurnalEffect = timeFactor * 9 * (1 - 0.5 * transitionRatio);
|
|
|
-
|
|
|
- // 随机波动(过渡期间减小)
|
|
|
- double randomEffect = (random.nextDouble() - 0.5) * 6 * (1 - 0.7 * transitionRatio);
|
|
|
-
|
|
|
- // 计算目标湿度
|
|
|
- double targetHumidity = baseHumidity - diurnalEffect + randomEffect;
|
|
|
-
|
|
|
- // 应用变化率限制(过渡期间更严格)
|
|
|
- double maxDelta = 0.8 + 0.7 * (1 - transitionRatio); // 0.8~1.5%
|
|
|
- targetHumidity = Math.max(previousHumidity - maxDelta,
|
|
|
- Math.min(previousHumidity + maxDelta, targetHumidity));
|
|
|
-
|
|
|
- // 确保在合理范围内
|
|
|
- return Math.max(range[0], Math.min(range[1], targetHumidity));
|
|
|
- }
|
|
|
-
|
|
|
- // 新增:平滑污染物生成(通用方法)
|
|
|
- private static double generateSmoothPollutant(int[] range, Random random,
|
|
|
- double previousValue, double transitionRatio,
|
|
|
- double baseDelta, double fluctuationFactor) {
|
|
|
- // 基础值(考虑过渡比例)
|
|
|
- double baseValue = range[0] + (range[1] - range[0]) * random.nextDouble();
|
|
|
-
|
|
|
- // 随机波动(过渡期间减小)
|
|
|
- double randomEffect = (random.nextDouble() - 0.5) * fluctuationFactor * range[1] *
|
|
|
- (1 - 0.7 * transitionRatio);
|
|
|
-
|
|
|
- // 计算目标值
|
|
|
- double targetValue = baseValue + randomEffect;
|
|
|
-
|
|
|
- // 应用变化率限制(过渡期间更严格)
|
|
|
- double maxDelta = baseDelta + baseDelta * (1 - transitionRatio);
|
|
|
- targetValue = Math.max(previousValue - maxDelta,
|
|
|
- Math.min(previousValue + maxDelta, targetValue));
|
|
|
-
|
|
|
- // 确保在合理范围内
|
|
|
- return Math.max(range[0], Math.min(range[1], targetValue));
|
|
|
- }
|
|
|
-
|
|
|
- // 新增:平滑噪音生成(考虑昼夜节律)
|
|
|
- private static double generateSmoothNoise(int[] range, LocalDateTime time,
|
|
|
- Random random, double previousNoise,
|
|
|
- double transitionRatio) {
|
|
|
- // 基础噪音
|
|
|
- double baseNoise = range[0] + (range[1] - range[0]) * random.nextDouble();
|
|
|
-
|
|
|
- // 昼夜影响(白天高,夜晚低)
|
|
|
- double hour = time.getHour() + time.getMinute() / 60.0;
|
|
|
- double diurnalEffect = 0;
|
|
|
- if (hour >= 6 && hour <= 22) { // 白天(6:00-22:00)
|
|
|
- // 早晚高峰
|
|
|
- if ((hour >= 7 && hour <= 9) || (hour >= 17 && hour <= 19)) {
|
|
|
- diurnalEffect = 10 + 5 * Math.sin((hour - 8) * Math.PI / 6);
|
|
|
- } else {
|
|
|
- diurnalEffect = 5 * Math.sin((hour - 12) * Math.PI / 12);
|
|
|
- }
|
|
|
- } else { // 夜间
|
|
|
- diurnalEffect = -15;
|
|
|
- }
|
|
|
-
|
|
|
- // 随机波动
|
|
|
- double randomEffect = (random.nextDouble() - 0.5) * 5;
|
|
|
-
|
|
|
- // 计算目标噪音
|
|
|
- double targetNoise = baseNoise + diurnalEffect + randomEffect;
|
|
|
-
|
|
|
- // 应用变化率限制
|
|
|
- double maxDelta = 2.0 + 1.5 * (1 - transitionRatio); // 2.0~3.5 dB
|
|
|
- targetNoise = Math.max(previousNoise - maxDelta,
|
|
|
- Math.min(previousNoise + maxDelta, targetNoise));
|
|
|
-
|
|
|
- // 确保在合理范围内
|
|
|
- return Math.max(range[0], Math.min(range[1], targetNoise));
|
|
|
- }
|
|
|
-
|
|
|
- // 新增:平滑风速生成
|
|
|
- private static double generateSmoothWindSpeed(int[] range, Random random,
|
|
|
- double previousSpeed,
|
|
|
- double transitionRatio) {
|
|
|
- // 基础风速(考虑过渡比例)
|
|
|
- double baseSpeed = range[0] + (range[1] - range[0]) * random.nextDouble();
|
|
|
-
|
|
|
- // 随机波动(过渡期间减小)
|
|
|
- double randomEffect = (random.nextDouble() - 0.5) * 2.0 * (1 - 0.7 * transitionRatio);
|
|
|
-
|
|
|
- // 计算目标风速
|
|
|
- double targetSpeed = baseSpeed + randomEffect;
|
|
|
-
|
|
|
- // 应用变化率限制(过渡期间更严格)
|
|
|
- double maxDelta = 0.5 + 0.4 * (1 - transitionRatio); // 0.5~0.9 m/s
|
|
|
- targetSpeed = Math.max(previousSpeed - maxDelta,
|
|
|
- Math.min(previousSpeed + maxDelta, targetSpeed));
|
|
|
-
|
|
|
- // 确保在合理范围内
|
|
|
- return Math.max(range[0], Math.min(range[1], targetSpeed));
|
|
|
- }
|
|
|
-
|
|
|
- // 新增:平滑风向过渡
|
|
|
- private static WindDirection generateSmoothWindDirection(WindDirection currentDir,
|
|
|
- WindDirection targetDir,
|
|
|
- double transitionRatio,
|
|
|
- Random random) {
|
|
|
- // 如果已经是目标风向,或者过渡完成
|
|
|
- if (currentDir == targetDir || transitionRatio >= 1.0) {
|
|
|
- return targetDir;
|
|
|
- }
|
|
|
-
|
|
|
- // 有20%的概率在非过渡期间改变风向
|
|
|
- if (transitionRatio == 0 && random.nextDouble() < 0.2) {
|
|
|
- // 随机小幅变化(±1个方位)
|
|
|
- int currentIndex = currentDir.ordinal();
|
|
|
- int change = random.nextBoolean() ? 1 : -1;
|
|
|
- int newIndex = (currentIndex + change + WindDirection.values().length) % WindDirection.values().length;
|
|
|
- return WindDirection.values()[newIndex];
|
|
|
- }
|
|
|
-
|
|
|
- // 过渡期间:逐步转向目标风向
|
|
|
- int currentAngle = getDirectionAngle(currentDir);
|
|
|
- int targetAngle = getDirectionAngle(targetDir);
|
|
|
-
|
|
|
- // 计算最短转向路径
|
|
|
- int diff1 = (targetAngle - currentAngle + 360) % 360;
|
|
|
- int diff2 = diff1 - 360;
|
|
|
- int shortestDiff = Math.abs(diff1) < Math.abs(diff2) ? diff1 : diff2;
|
|
|
-
|
|
|
- // 最大转向角度(每次最多22.5度)
|
|
|
- int maxTurn = (int) (22 * transitionRatio);
|
|
|
- int actualTurn = Math.min(Math.abs(shortestDiff), maxTurn);
|
|
|
- if (shortestDiff < 0) actualTurn = -actualTurn;
|
|
|
-
|
|
|
- // 计算新角度
|
|
|
- int newAngle = (currentAngle + actualTurn + 360) % 360;
|
|
|
-
|
|
|
- // 转换为最接近的风向
|
|
|
- int index = (int) Math.round(newAngle / 22.5) % 16;
|
|
|
- return WindDirection.values()[index];
|
|
|
- }
|
|
|
-
|
|
|
- // 获取当前季节
|
|
|
- private static Season getSeason(LocalDateTime time) {
|
|
|
- int month = time.getMonthValue();
|
|
|
- if (month == 12 || month <= 2) return Season.WINTER;
|
|
|
- if (month <= 5) return Season.SPRING;
|
|
|
- if (month <= 8) return Season.SUMMER;
|
|
|
- return Season.AUTUMN;
|
|
|
- }
|
|
|
-
|
|
|
-// public static void main(String[] args) {
|
|
|
-// int year = 2024;
|
|
|
-// List<WeatherData> yearlyData = simulateYearlyWeather(year);
|
|
|
-//
|
|
|
-// // 打印前3天的数据(每小时间隔)
|
|
|
-// System.out.println("时间戳\t\t\t\t温度\t湿度\t天气状态\tPM2.5\tPM10\tPM100\t噪音\t风速和风向");
|
|
|
-// for (int i = 0; i < 480; i += 20) { // 每20个点 = 1小时 (3分钟间隔)
|
|
|
-// WeatherData data = yearlyData.get(i);
|
|
|
-// System.out.println(data);
|
|
|
-// }
|
|
|
-// System.out.println(yearlyData.size());
|
|
|
-
|
|
|
- // 统计不同天气下的平均风速
|
|
|
-// Map<WeatherState, Double> avgWindSpeedByWeather = yearlyData.stream()
|
|
|
-// .collect(Collectors.groupingBy(WeatherData::getWeatherState,
|
|
|
-// Collectors.averagingDouble(WeatherData::getWindSpeed)));
|
|
|
-//
|
|
|
-// System.out.println("\n不同天气状态下的平均风速:");
|
|
|
-// avgWindSpeedByWeather.forEach((state, avg) ->
|
|
|
-// System.out.printf("%s: %.1f m/s%n", state, avg));
|
|
|
-
|
|
|
- // 统计主导风向分布
|
|
|
-// Map<WindDirection, Long> windDirectionDistribution = yearlyData.stream()
|
|
|
-// .collect(Collectors.groupingBy(WeatherData::getWindDirection, Collectors.counting()));
|
|
|
-
|
|
|
-// System.out.println("\n风向分布:");
|
|
|
-// windDirectionDistribution.entrySet().stream()
|
|
|
-// .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
|
|
|
-// .forEach(entry ->
|
|
|
-// System.out.printf("%s: %.1f%%%n", entry.getKey(),
|
|
|
-// entry.getValue() * 100.0 / yearlyData.size()));
|
|
|
-// }
|
|
|
-}
|