comparison artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/collision/CollisionCalculation.java @ 9613:f2473dc34535

Nachtrag Pos. 19: Q calculation with historical discharge tables instead of master discharge table
author mschaefer
date Tue, 08 Oct 2019 15:03:24 +0200
parents 8b457ca70512
children 1d4262a68f1f
comparison
equal deleted inserted replaced
9602:6b2496d71936 9613:f2473dc34535
9 */ 9 */
10 package org.dive4elements.river.artifacts.sinfo.collision; 10 package org.dive4elements.river.artifacts.sinfo.collision;
11 11
12 import java.util.ArrayList; 12 import java.util.ArrayList;
13 import java.util.Collection; 13 import java.util.Collection;
14 import java.util.Date;
14 import java.util.HashMap; 15 import java.util.HashMap;
15 import java.util.List; 16 import java.util.List;
16 import java.util.Map; 17 import java.util.Map;
18 import java.util.Map.Entry;
17 import java.util.NavigableSet; 19 import java.util.NavigableSet;
20 import java.util.TreeMap;
18 import java.util.TreeSet; 21 import java.util.TreeSet;
19 22
20 import org.apache.commons.lang.math.DoubleRange; 23 import org.apache.commons.lang.math.DoubleRange;
21 import org.dive4elements.artifacts.CallContext; 24 import org.dive4elements.artifacts.CallContext;
22 import org.dive4elements.river.artifacts.common.GeneralResultType; 25 import org.dive4elements.river.artifacts.common.GeneralResultType;
30 import org.dive4elements.river.artifacts.sinfo.common.GaugeMainValueFinder; 33 import org.dive4elements.river.artifacts.sinfo.common.GaugeMainValueFinder;
31 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType; 34 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType;
32 import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils; 35 import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils;
33 import org.dive4elements.river.artifacts.sinfo.util.RiverInfo; 36 import org.dive4elements.river.artifacts.sinfo.util.RiverInfo;
34 import org.dive4elements.river.backend.utils.DateUtil; 37 import org.dive4elements.river.backend.utils.DateUtil;
38 import org.dive4elements.river.model.DischargeTable;
39 import org.dive4elements.river.model.Gauge;
35 import org.dive4elements.river.model.MainValueType.MainValueTypeKey; 40 import org.dive4elements.river.model.MainValueType.MainValueTypeKey;
36 import org.dive4elements.river.model.River; 41 import org.dive4elements.river.model.River;
37 import org.dive4elements.river.model.sinfo.CollisionAggregateValue; 42 import org.dive4elements.river.model.sinfo.CollisionAggregateValue;
38 import org.dive4elements.river.model.sinfo.CollisionValue; 43 import org.dive4elements.river.model.sinfo.CollisionValue;
39 44
41 46
42 // private static Logger log = Logger.getLogger(FloodDurationCalculation.class); 47 // private static Logger log = Logger.getLogger(FloodDurationCalculation.class);
43 48
44 private final CallContext context; 49 private final CallContext context;
45 50
51 private CollisionAccess access;
52 private River river;
53 private List<DateRange> years;
54 private Calculation problems;
55 private int qfinderProblemCount;
56
46 public CollisionCalculation(final CallContext context) { 57 public CollisionCalculation(final CallContext context) {
47 this.context = context; 58 this.context = context;
48 } 59 }
49 60
50 public CalculationResult calculate(final SINFOArtifact sinfo) { 61 public CalculationResult calculate(final SINFOArtifact sinfo) {
51 62
52 final String user = CalculationUtils.findArtifactUser(this.context, sinfo); 63 final String user = CalculationUtils.findArtifactUser(this.context, sinfo);
53 64
54 // access input data 65 // access input data
55 final CollisionAccess access = new CollisionAccess(sinfo); 66 this.access = new CollisionAccess(sinfo);
56 final River river = access.getRiver(); 67 this.river = this.access.getRiver();
57 final RiverInfo riverInfo = new RiverInfo(river); 68 final RiverInfo riverInfo = new RiverInfo(this.river);
58 69 final DoubleRange calcRange = this.access.getRange();
59 final DoubleRange calcRange = access.getRange();
60 70
61 // calculate results for each year or epoch 71 // calculate results for each year or epoch
62 final Calculation problems = new Calculation(); 72 this.problems = new Calculation();
63 73 this.qfinderProblemCount = 0;
64 final String calcModeLabel = Resources.getMsg(this.context.getMeta(), sinfo.getCalculationMode().name()); 74 final String calcModeLabel = Resources.getMsg(this.context.getMeta(), sinfo.getCalculationMode().name());
65 75
66 final CollisionCalculationResults results = new CollisionCalculationResults(calcModeLabel, user, riverInfo, calcRange, access.getYearsHeader()); 76 final CollisionCalculationResults results = new CollisionCalculationResults(calcModeLabel, user, riverInfo, calcRange, this.access.getYearsHeader());
67 77
68 final Collection<ResultRow> overViewRows = new ArrayList<>(); 78 final Collection<ResultRow> overViewRows = new ArrayList<>();
69 79
70 final List<DateRange> years = new ArrayList<>(); 80 this.years = new ArrayList<>();
71 final NavigableSet<Integer> detailYears = new TreeSet<>(); 81 final NavigableSet<Integer> detailYears = new TreeSet<>();
72 if (access.getYears() != null) { 82 if (this.access.getYears() != null) {
73 for (final int year : access.getYears()) { 83 for (final int year : this.access.getYears()) {
74 calculateOverview(overViewRows, river, access.getLowerKm(), access.getUpperKm(), year, year, false); 84 calculateOverview(overViewRows, year, year, false);
75 years.add(new DateRange(DateUtil.getStartDateFromYear(year), DateUtil.getEndDateFromYear(year))); 85 this.years.add(new DateRange(DateUtil.getStartDateFromYear(year), DateUtil.getEndDateFromYear(year)));
76 detailYears.add(Integer.valueOf(year)); 86 detailYears.add(Integer.valueOf(year));
77 } 87 }
78 } else { 88 } else {
79 for (final DateRange dr : access.getEpochs()) { 89 for (final DateRange dr : this.access.getEpochs()) {
80 calculateOverview(overViewRows, river, access.getLowerKm(), access.getUpperKm(), dr.getFromYear(), dr.getToYear(), true); 90 calculateOverview(overViewRows, dr.getFromYear(), dr.getToYear(), true);
81 years.add(dr); 91 this.years.add(dr);
82 detailYearsAdd(detailYears, dr); 92 detailYearsAdd(detailYears, dr);
83 } 93 }
84 } 94 }
85 final CollisionCalcOverviewResult overviewResult = new CollisionCalcOverviewResult(access.getYearsHeader(), (access.getYears() == null), years, 95 final CollisionCalcOverviewResult overviewResult = new CollisionCalcOverviewResult(this.access.getYearsHeader(), (this.access.getYears() == null),
86 overViewRows); 96 this.years, overViewRows);
87 results.addResult(overviewResult, problems); 97 results.addResult(overviewResult, this.problems);
88 98
89 // calculate secondary results for each year 99 // calculate secondary results for each year
90 final Map<String, GaugeDischargeValuesFinder> qFinders = new HashMap<>(); 100 final Map<String, TreeMap<Date, GaugeDischargeValuesFinder>> qFinders = new HashMap<>();
91 final Map<String, GaugeMainValueFinder> zoneFinders = new HashMap<>(); 101 final Map<String, GaugeMainValueFinder> zoneFinders = new HashMap<>();
92 final Collection<ResultRow> detailsRows = new ArrayList<>(); 102 final Collection<ResultRow> detailsRows = new ArrayList<>();
93 for (final Integer year : detailYears) 103 for (final Integer year : detailYears)
94 calculateDetails(detailsRows, river, access.getLowerKm(), access.getUpperKm(), year, qFinders, zoneFinders, problems); 104 calculateDetails(detailsRows, year, qFinders, zoneFinders);
95 final CollisionCalcDetailResult detailResult = new CollisionCalcDetailResult("Details", detailsRows); 105 final CollisionCalcDetailResult detailResult = new CollisionCalcDetailResult("Details", detailsRows);
96 results.addResult(detailResult, problems); 106 results.addResult(detailResult, this.problems);
97 107
98 return new CalculationResult(results, problems); 108 return new CalculationResult(results, this.problems);
99 } 109 }
100 110
101 /** 111 /**
102 * Adds all years of an epoch to a set 112 * Adds all years of an epoch to a set
103 */ 113 */
108 118
109 /** 119 /**
110 * Calculates the collision counts for a km range of a river and a year or year range (epoch), 120 * Calculates the collision counts for a km range of a river and a year or year range (epoch),
111 * and adds them to a ResultRow collection 121 * and adds them to a ResultRow collection
112 */ 122 */
113 private void calculateOverview(final Collection<ResultRow> rows, final River river, final double fromKm, final double toKm, final int fromYear, 123 private void calculateOverview(final Collection<ResultRow> rows, final int fromYear, final int toYear, final boolean isEpoch) {
114 final int toYear, final boolean isEpoch) { 124 for (final CollisionAggregateValue aggregate : CollisionAggregateValue.getValuesByKm(this.river, this.access.getLowerKm(), this.access.getUpperKm(),
115 for (final CollisionAggregateValue aggregate : CollisionAggregateValue.getValuesByKm(river, fromKm, toKm, fromYear, toYear)) { 125 fromYear, toYear)) {
116 rows.add(ResultRow.create().putValue(GeneralResultType.station, aggregate.getStation()) 126 rows.add(ResultRow.create().putValue(GeneralResultType.station, aggregate.getStation())
117 .putValue(SInfoResultType.years, yearsToString(isEpoch, fromYear, toYear)).putValue(SInfoResultType.collisionCount, aggregate.getCount())); 127 .putValue(SInfoResultType.years, yearsToString(isEpoch, fromYear, toYear)).putValue(SInfoResultType.collisionCount, aggregate.getCount()));
118 } 128 }
119 } 129 }
120 130
133 } 143 }
134 144
135 /** 145 /**
136 * Calculates the collision details for a km range of a river and a year, and adds them to a ResultRow collection 146 * Calculates the collision details for a km range of a river and a year, and adds them to a ResultRow collection
137 */ 147 */
138 private void calculateDetails(final Collection<ResultRow> rows, final River river, final double fromKm, final double toKm, final int year, 148 private void calculateDetails(final Collection<ResultRow> rows, final int year, final Map<String, TreeMap<Date, GaugeDischargeValuesFinder>> qFinders,
139 final Map<String, GaugeDischargeValuesFinder> qFinders, final Map<String, GaugeMainValueFinder> zoneFinders, final Calculation problems) { 149 final Map<String, GaugeMainValueFinder> zoneFinders) {
140 150
141 for (final CollisionValue collision : CollisionValue.getValues(river, fromKm, toKm, DateUtil.getStartDateFromYear(year), 151 for (final CollisionValue collision : CollisionValue.getValues(this.river, this.access.getLowerKm(), this.access.getUpperKm(),
142 DateUtil.getEndDateFromYear(year))) { 152 DateUtil.getStartDateFromYear(year), DateUtil.getEndDateFromYear(year))) {
143 final String gaugeName = collision.getGaugeName(); 153 final String gaugeName = collision.getGaugeName();
144 final double q = getQ(qFinders, gaugeName, collision.getGaugeW().doubleValue(), river, problems); 154 final double q = getQ(qFinders, gaugeName, collision.getGaugeW().doubleValue(), collision.getEventDate());
145 final double qOut = Double.isInfinite(q) ? Double.NaN : q; 155 final double qOut = Double.isInfinite(q) ? Double.NaN : q;
146 final String zone = getZone(zoneFinders, gaugeName, q, river, problems); 156 final String zone = getZone(zoneFinders, gaugeName, q);
147 rows.add(ResultRow.create().putValue(GeneralResultType.station, collision.getStation()) 157 rows.add(ResultRow.create().putValue(GeneralResultType.station, collision.getStation())
148 .putValue(GeneralResultType.dateShort, collision.getEventDate()).putValue(SInfoResultType.collisionGaugeW, collision.getGaugeW()) 158 .putValue(GeneralResultType.dateShort, collision.getEventDate()).putValue(SInfoResultType.collisionGaugeW, collision.getGaugeW())
149 .putValue(GeneralResultType.gaugeLabel, gaugeName).putValue(SInfoResultType.dischargeLong, qOut) 159 .putValue(GeneralResultType.gaugeLabel, gaugeName).putValue(SInfoResultType.dischargeLong, qOut)
150 .putValue(SInfoResultType.dischargeZone, zone)); 160 .putValue(SInfoResultType.dischargeZone, zone));
151 } 161 }
152 } 162 }
153 163
154 /** 164 /**
155 * Gets the discharge of a gauge and a W 165 * Gets the discharge of a gauge and a W
156 */ 166 */
157 private double getQ(final Map<String, GaugeDischargeValuesFinder> qFinders, final String gaugeName, final double w, final River river, 167 private double getQ(final Map<String, TreeMap<Date, GaugeDischargeValuesFinder>> qFinders, final String gaugeName, final double w, final Date when) {
158 final Calculation problems) {
159 // Find the gauge and load its discharge table, if not already in the map 168 // Find the gauge and load its discharge table, if not already in the map
160 final String gnKey = gaugeName.toLowerCase(); 169 final String gnKey = gaugeName.toLowerCase();
161 if (!qFinders.containsKey(gnKey)) 170 if (!qFinders.containsKey(gnKey))
162 qFinders.put(gnKey, GaugeDischargeValuesFinder.loadValues(river, gaugeName, problems)); 171 addQFinders(qFinders, gaugeName);
163 // Interpolate W. 172 // Interpolate W.
164 if (qFinders.get(gnKey) == null) 173 final GaugeDischargeValuesFinder qFinder = getQFinder(qFinders.get(gnKey), when);
174 if (qFinder == null) {
175 this.qfinderProblemCount++;
176 if (this.qfinderProblemCount == 1)
177 this.problems.addProblem("gauge_discharge_table.missing", gaugeName);
165 return Double.NaN; 178 return Double.NaN;
166 return qFinders.get(gnKey).getDischarge(w); 179 }
180 return qFinder.getDischarge(w);
181 }
182
183 /**
184 * Add the discharge finders for a gauge and the active time period
185 */
186 private void addQFinders(final Map<String, TreeMap<Date, GaugeDischargeValuesFinder>> qFinders, final String gaugeName) {
187 final String gnKey = gaugeName.toLowerCase();
188 final Gauge gauge = this.river.determineGaugeByName(gaugeName);
189 if (gauge == null) {
190 qFinders.put(gnKey, new TreeMap<Date, GaugeDischargeValuesFinder>());
191 return;
192 }
193 final List<DischargeTable> qtables = DischargeTable.fetchHistoricalDischargeTables(gauge, this.years.get(0).getFrom(),
194 this.years.get(this.years.size() - 1).getTo());
195 qFinders.put(gnKey, new TreeMap<Date, GaugeDischargeValuesFinder>());
196 for (final DischargeTable qtable : qtables)
197 qFinders.get(gnKey).put(qtable.getTimeInterval().getStartTime(),
198 GaugeDischargeValuesFinder.loadValues(qtable, this.river, gaugeName, this.problems));
199 }
200
201 /**
202 * Searches a q values finder map for a date time
203 */
204 private GaugeDischargeValuesFinder getQFinder(final TreeMap<Date, GaugeDischargeValuesFinder> qFinders, final Date when) {
205 if (qFinders.containsKey(when))
206 return qFinders.get(when);
207 final Entry<Date, GaugeDischargeValuesFinder> found = qFinders.floorEntry(when);
208 if (found == null)
209 return null;
210 if ((found.getValue().getEndTime() != null) && found.getValue().getEndTime().before(when))
211 return null;
212 return found.getValue();
167 } 213 }
168 214
169 /** 215 /**
170 * Gets the main value zone name of a gauge and a Q 216 * Gets the main value zone name of a gauge and a Q
171 */ 217 */
172 private String getZone(final Map<String, GaugeMainValueFinder> zoneFinders, final String gaugeName, final double q, final River river, 218 private String getZone(final Map<String, GaugeMainValueFinder> zoneFinders, final String gaugeName, final double q) {
173 final Calculation problems) {
174 // Find the gauge and load its main value list, if not already in the map 219 // Find the gauge and load its main value list, if not already in the map
175 final String gnKey = gaugeName.toLowerCase(); 220 final String gnKey = gaugeName.toLowerCase();
176 if (!zoneFinders.containsKey(gnKey)) 221 if (!zoneFinders.containsKey(gnKey))
177 zoneFinders.put(gnKey, GaugeMainValueFinder.loadValues(MainValueTypeKey.Q, river, gaugeName, problems, "GLQ")); 222 zoneFinders.put(gnKey, GaugeMainValueFinder.loadValues(MainValueTypeKey.Q, this.river, gaugeName, this.problems, "GLQ"));
178 // Build the zone name 223 // Build the zone name
179 if (zoneFinders.get(gnKey) == null) 224 if (zoneFinders.get(gnKey) == null)
180 return ""; 225 return "";
181 return zoneFinders.get(gnKey).findZoneName(q); 226 return zoneFinders.get(gnKey).findZoneName(q);
182 } 227 }

http://dive4elements.wald.intevation.org