comparison artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculator.java @ 9269:83ebeb620b5a

Station specific main value annotations in S-Info flood duration curve, corrected infrastructure flood duration calculation
author mschaefer
date Thu, 19 Jul 2018 08:07:03 +0200
parents 465347d12990
children bcbae86ce7b3
comparison
equal deleted inserted replaced
9268:ae9dee74e43e 9269:83ebeb620b5a
25 import org.dive4elements.river.artifacts.model.Calculation; 25 import org.dive4elements.river.artifacts.model.Calculation;
26 import org.dive4elements.river.artifacts.model.Calculation.Problem; 26 import org.dive4elements.river.artifacts.model.Calculation.Problem;
27 import org.dive4elements.river.artifacts.model.CalculationResult; 27 import org.dive4elements.river.artifacts.model.CalculationResult;
28 import org.dive4elements.river.artifacts.model.WQDay; 28 import org.dive4elements.river.artifacts.model.WQDay;
29 import org.dive4elements.river.artifacts.model.WQKms; 29 import org.dive4elements.river.artifacts.model.WQKms;
30 import org.dive4elements.river.artifacts.model.WstValueTable;
31 import org.dive4elements.river.artifacts.model.WstValueTable.QPosition;
32 import org.dive4elements.river.artifacts.model.WstValueTableFactory;
30 import org.dive4elements.river.artifacts.resources.Resources; 33 import org.dive4elements.river.artifacts.resources.Resources;
31 import org.dive4elements.river.artifacts.sinfo.common.GaugeDurationValuesFinder; 34 import org.dive4elements.river.artifacts.sinfo.common.GaugeDurationValuesFinder;
32 import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider; 35 import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider;
33 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType; 36 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType;
34 import org.dive4elements.river.artifacts.sinfo.common.WQBaseTableFinder;
35 import org.dive4elements.river.artifacts.sinfo.flood_duration.RiversideRadioChoice.RiversideChoiceKey; 37 import org.dive4elements.river.artifacts.sinfo.flood_duration.RiversideRadioChoice.RiversideChoiceKey;
36 import org.dive4elements.river.exports.WaterlevelDescriptionBuilder; 38 import org.dive4elements.river.exports.WaterlevelDescriptionBuilder;
37 import org.dive4elements.river.jfree.StickyAxisAnnotation; 39 import org.dive4elements.river.jfree.StickyAxisAnnotation;
38 import org.dive4elements.river.jfree.StickyAxisAnnotation.SimpleAxis; 40 import org.dive4elements.river.jfree.StickyAxisAnnotation.SimpleAxis;
39 import org.dive4elements.river.model.Attribute.AttributeKey; 41 import org.dive4elements.river.model.Attribute.AttributeKey;
88 addGaugeLimits(allStations, durFinders.keySet(), calcRange.getMinimumDouble(), calcRange.getMaximumDouble()); 90 addGaugeLimits(allStations, durFinders.keySet(), calcRange.getMinimumDouble(), calcRange.getMaximumDouble());
89 addInfrastructures(allStations, secondBank, infras); 91 addInfrastructures(allStations, secondBank, infras);
90 final double[] stationsSorted = sortStations(allStations.keySet()); 92 final double[] stationsSorted = sortStations(allStations.keySet());
91 93
92 // Calculate W and Q for all stations and the selected discharge states 94 // Calculate W and Q for all stations and the selected discharge states
93 // TODO Laut Herrn Reiß: Q und D jeweils konstant für jedes Pegel-Intervall, Q-Änderungen (Zuflüsse etc.) aus .wst 95 // TODO Geht das schneller, wenn man WstValueTable statt WINFOArtifact.computeWaterlevelData nutzt?
94 // ignorieren
95 final WQKms[] wqkmsArray = calculateWaterlevels(winfo, stationsSorted, problems); 96 final WQKms[] wqkmsArray = calculateWaterlevels(winfo, stationsSorted, problems);
96 97
97 // Determine discharge state labels of the main values 98 // Determine discharge state labels of the main values
98 updateMainValueLabels(wqkmsArray, winfo, problems); 99 updateMainValueLabels(wqkmsArray, winfo, problems);
99 100
100 // Create a finder for Q in the {river}.wst km-w-q table 101 // Load base wst table (river).wst - first run takes long time, then it's cached
101 final WQBaseTableFinder wqFinder = WQBaseTableFinder.loadValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(), 102 final WstValueTable wst = WstValueTableFactory.getTable(this.riverInfoProvider.getRiver());
102 calcRange.getMaximumDouble(), problems);
103 103
104 // Calculate the durations and create the result rows 104 // Calculate the durations and create the result rows
105 for (int i = 0; i <= stationsSorted.length - 1; i++) { 105 for (int i = 0; i <= stationsSorted.length - 1; i++) {
106 final Gauge gauge = this.riverInfoProvider.getGauge(stationsSorted[i], true); 106 final Gauge gauge = this.riverInfoProvider.getGauge(stationsSorted[i], true);
107 final ResultRow row = createRow(stationsSorted[i], gauge, firstGauge, wqkmsArray, durFinders.get(gauge), i); 107 final ResultRow row = createRow(stationsSorted[i], gauge, firstGauge, wqkmsArray, durFinders.get(gauge), i);
108 if (allStations.containsKey(stationsSorted[i]) && (allStations.get(stationsSorted[i]) != null)) 108 if (allStations.containsKey(stationsSorted[i]) && (allStations.get(stationsSorted[i]) != null))
109 calculateInfrastructure(row, gauge, allStations.get(stationsSorted[i]), wqFinder, durFinders); 109 calculateInfrastructure(row, gauge, allStations.get(stationsSorted[i]), wst, durFinders);
110 this.rows.add(row); 110 this.rows.add(row);
111 if (secondBank.containsKey(stationsSorted[i])) { 111 if (secondBank.containsKey(stationsSorted[i])) {
112 final ResultRow row2 = ResultRow.create(row); 112 final ResultRow row2 = ResultRow.create(row);
113 calculateInfrastructure(row2, gauge, secondBank.get(stationsSorted[i]), wqFinder, durFinders); 113 calculateInfrastructure(row2, gauge, secondBank.get(stationsSorted[i]), wst, durFinders);
114 this.rows.add(row2); 114 this.rows.add(row2);
115 } 115 }
116 } 116 }
117
118 //// Create a finder for Q in the {river}.wst km-w-q table
119 // final WQBaseTableFinder wqFinder = WQBaseTableFinder.loadValues(this.riverInfoProvider.getRiver(),
120 //// calcRange.getMinimumDouble(),
121 // calcRange.getMaximumDouble(), problems);
122 //
123 //// Calculate the durations and create the result rows
124 // for (int i = 0; i <= stationsSorted.length - 1; i++) {
125 // final Gauge gauge = this.riverInfoProvider.getGauge(stationsSorted[i], true);
126 // final ResultRow row = createRow(stationsSorted[i], gauge, firstGauge, wqkmsArray, durFinders.get(gauge), i);
127 // if (allStations.containsKey(stationsSorted[i]) && (allStations.get(stationsSorted[i]) != null))
128 // calculateInfrastructure(row, gauge, allStations.get(stationsSorted[i]), wqFinder, durFinders);
129 // this.rows.add(row);
130 // if (secondBank.containsKey(stationsSorted[i])) {
131 // final ResultRow row2 = ResultRow.create(row);
132 // calculateInfrastructure(row2, gauge, secondBank.get(stationsSorted[i]), wqFinder, durFinders);
133 // this.rows.add(row2);
134 // }
135 // }
136
117 final String[] mainValueLabels = new String[wqkmsArray.length]; 137 final String[] mainValueLabels = new String[wqkmsArray.length];
118 for (int i = 0; i <= wqkmsArray.length - 1; i++) 138 for (int i = 0; i <= wqkmsArray.length - 1; i++)
119 mainValueLabels[i] = wqkmsArray[i].getName(); 139 mainValueLabels[i] = wqkmsArray[i].getName();
120 results.addResult(new FloodDurationCalculationResult(label, mainValueLabels, this.rows), problems); 140 results.addResult(new FloodDurationCalculationResult(label, mainValueLabels, this.rows), problems);
121
122 // calcWQDays(problems, stationsSorted[0], AttributeKey.LEFT, winfo, results);
123 //
124 // calcMainValueAnnotations(problems, 0, AttributeKey.LEFT, wqkmsArray, results);
125 //
126 // calcInfrastructureAnnotations(problems, this.rows.get(0), wqkmsArray, results);
127 } 141 }
128 142
129 /** 143 /**
130 * Calculates the duration curve for a station 144 * Calculates the duration curve for a station
131 */ 145 */
146 } 160 }
147 return new WQDay(days, ws, qs); 161 return new WQDay(days, ws, qs);
148 } 162 }
149 163
150 /** 164 /**
151 * Calculate the data for the W and Q main value lines in the duration curve chart and add to result collection 165 * Calculates the data for the Q main value lines in the duration curve chart
152 */ 166 */
153 public void calcMainValueAnnotations(final Calculation problems, final int stationIndex, final AttributeKey riverside, 167 public List<StickyAxisAnnotation> calcMainValueQAnnotations(final Calculation problems, final double station, final FloodDurationCalculationResult result) {
154 final WQKms[] wqkmsArray, final FloodDurationCalculationResults results) {
155
156 // Same way as in MainValueWFacet and ..QFacet, but special label handling
157 final List<StickyAxisAnnotation> ws = new ArrayList<>();
158 final List<StickyAxisAnnotation> qs = new ArrayList<>();
159 for (int i = 0; i <= wqkmsArray.length - 1; i++) {
160 final String qLabel = wqkmsArray[i].getName().startsWith("W=") ? "Q(" + wqkmsArray[i].getName() + ")" : wqkmsArray[i].getName();
161 final StickyAxisAnnotation qAnnotation = new StickyAxisAnnotation(qLabel, (float) wqkmsArray[i].getQ(stationIndex), SimpleAxis.Y_AXIS,
162 FloodDurationCurveGenerator.YAXIS.Q.idx);
163 qs.add(qAnnotation);
164 FloodDurationMainValuesQFacet.setHitPoint(results.getDurationCurve(), qAnnotation);
165 final String wLabel = !wqkmsArray[i].getName().startsWith("W=") ? "W(" + wqkmsArray[i].getName() + ")" : wqkmsArray[i].getName();
166 final StickyAxisAnnotation wAnnotation = new StickyAxisAnnotation(wLabel, (float) wqkmsArray[i].getW(stationIndex), SimpleAxis.Y_AXIS,
167 FloodDurationCurveGenerator.YAXIS.W.idx);
168 ws.add(wAnnotation);
169 FloodDurationMainValuesWFacet.setHitPoint(results.getDurationCurve(), wAnnotation);
170 }
171 results.getMainValueQAnnotations().clear();
172 results.getMainValueQAnnotations().addAll(qs);
173 results.getMainValueWAnnotations().clear();
174 results.getMainValueWAnnotations().addAll(ws);
175 }
176
177 /**
178 * Calculate the data for the W and Q lines in the duration curve chart for the infrastructure height and add to result
179 * collection
180 */
181 public List<StickyAxisAnnotation> calcInfrastructureAnnotations(final Calculation problems, final double station,
182 final FloodDurationCalculationResult result) {
183 168
184 // Search the station in the previously calculated result rows and terminate if no infrastructure row found 169 // Search the station in the previously calculated result rows and terminate if no infrastructure row found
185 double station1 = station; 170 double station1 = station;
186 if (Double.isNaN(station)) { 171 if (Double.isNaN(station)) {
187 for (final ResultRow row : result.getRows()) { 172 for (final ResultRow row : result.getRows()) {
189 break; 174 break;
190 } 175 }
191 } 176 }
192 final List<ResultRow> stationRows = searchStation(station1, result.getRows()); 177 final List<ResultRow> stationRows = searchStation(station1, result.getRows());
193 if (stationRows.isEmpty() || (stationRows.get(0).getValue(SInfoResultType.infrastructuretype) == null)) { 178 if (stationRows.isEmpty() || (stationRows.get(0).getValue(SInfoResultType.infrastructuretype) == null)) {
194 return null; 179 return new ArrayList<>();
180 }
181 final ResultRow row = stationRows.get(0);
182 final List<StickyAxisAnnotation> annotations = new ArrayList<>();
183 final List<DurationWaterlevel> wqds = (List<DurationWaterlevel>) row.getValue(SInfoResultType.customMultiRowColWaterlevel);
184 for (final DurationWaterlevel wqd : wqds) {
185 final String label = wqd.getBezeichnung().startsWith("W=") ? "Q(" + wqd.getBezeichnung() + ")" : wqd.getBezeichnung();
186 final StickyAxisAnnotation annotation = new StickyAxisAnnotation(label, (float) wqd.getDischarge(), SimpleAxis.Y_AXIS,
187 FloodDurationCurveGenerator.YAXIS.Q.idx);
188 annotation.setHitPoint(wqd.getFloodDurDaysPerYear());
189 annotations.add(annotation);
190 }
191 return annotations;
192 }
193
194 /**
195 * Calculates the data for the W main value lines in the duration curve chart
196 */
197 public List<StickyAxisAnnotation> calcMainValueWAnnotations(final Calculation problems, final double station, final FloodDurationCalculationResult result) {
198
199 // Search the station in the previously calculated result rows and terminate if no infrastructure row found
200 double station1 = station;
201 if (Double.isNaN(station)) {
202 for (final ResultRow row : result.getRows()) {
203 station1 = row.getDoubleValue(GeneralResultType.station);
204 break;
205 }
206 }
207 final List<ResultRow> stationRows = searchStation(station1, result.getRows());
208 if (stationRows.isEmpty() || (stationRows.get(0).getValue(SInfoResultType.infrastructuretype) == null)) {
209 return new ArrayList<>();
210 }
211 final List<StickyAxisAnnotation> annotations = new ArrayList<>();
212 final ResultRow row = stationRows.get(0);
213 final List<DurationWaterlevel> wqds = (List<DurationWaterlevel>) row.getValue(SInfoResultType.customMultiRowColWaterlevel);
214 for (final DurationWaterlevel wqd : wqds) {
215 final String label = !wqd.getBezeichnung().startsWith("W=") ? "W(" + wqd.getBezeichnung() + ")" : wqd.getBezeichnung();
216 final StickyAxisAnnotation annotation = new StickyAxisAnnotation(label, (float) wqd.getWaterlevel(), SimpleAxis.Y_AXIS,
217 FloodDurationCurveGenerator.YAXIS.W.idx);
218 annotation.setHitPoint(wqd.getFloodDurDaysPerYear());
219 annotations.add(annotation);
220 }
221 return annotations;
222 }
223
224 /**
225 * Calculate the data for the W and Q lines in the duration curve chart for the infrastructure height and add to result
226 * collection
227 */
228 public List<StickyAxisAnnotation> calcInfrastructureAnnotations(final Calculation problems, final double station,
229 final FloodDurationCalculationResult result) {
230
231 // Search the station in the previously calculated result rows and terminate if no infrastructure row found
232 double station1 = station;
233 if (Double.isNaN(station)) {
234 for (final ResultRow row : result.getRows()) {
235 station1 = row.getDoubleValue(GeneralResultType.station);
236 break;
237 }
238 }
239 final List<ResultRow> stationRows = searchStation(station1, result.getRows());
240 if (stationRows.isEmpty() || (stationRows.get(0).getValue(SInfoResultType.infrastructuretype) == null)) {
241 return new ArrayList<>();
195 } 242 }
196 // Same way as in MainValueWFacet and ..QFacet 243 // Same way as in MainValueWFacet and ..QFacet
197 final List<StickyAxisAnnotation> annotations = new ArrayList<>(); 244 final List<StickyAxisAnnotation> annotations = new ArrayList<>();
198 for (final ResultRow row : stationRows) { 245 for (final ResultRow row : stationRows) {
199 annotations.add(calcWAnnotation(row)); 246 annotations.add(calcInfrastructureWAnnotation(row));
200 annotations.add(calcQAnnotation(row)); 247 annotations.add(calcInfrastructureQAnnotation(row));
201 } 248 }
202 return annotations; 249 return annotations;
203 } 250 }
204 251
205 /** 252 /**
217 } 264 }
218 265
219 /** 266 /**
220 * Calculates the Q annotation lines of an infrastructure 267 * Calculates the Q annotation lines of an infrastructure
221 */ 268 */
222 private StickyAxisAnnotation calcQAnnotation(final ResultRow row) { 269 private StickyAxisAnnotation calcInfrastructureQAnnotation(final ResultRow row) {
223 final String label = Resources.getMsg(this.context.getMeta(), "sinfo.chart.flood_duration.curve.infrastructure", 270 final String label = Resources.getMsg(this.context.getMeta(), "sinfo.chart.flood_duration.curve.infrastructure",
224 "sinfo.chart.flood_duration.curve.infrastructure", 271 "sinfo.chart.flood_duration.curve.infrastructure",
225 SInfoResultType.infrastructuretype.exportValue(this.context, row.getValue(SInfoResultType.infrastructuretype)) 272 SInfoResultType.infrastructuretype.exportValue(this.context, row.getValue(SInfoResultType.infrastructuretype))
226 + ", " + SInfoResultType.riverside.exportValue(this.context, row.getValue(SInfoResultType.riverside))); 273 + ", " + SInfoResultType.riverside.exportValue(this.context, row.getValue(SInfoResultType.riverside)));
227 final StickyAxisAnnotation annotation = new StickyAxisAnnotation(label, (float) row.getDoubleValue(SInfoResultType.floodDischarge), 274 final StickyAxisAnnotation annotation = new StickyAxisAnnotation(label, (float) row.getDoubleValue(SInfoResultType.floodDischarge),
231 } 278 }
232 279
233 /** 280 /**
234 * Calculates the W annotation lines of an infrastructure 281 * Calculates the W annotation lines of an infrastructure
235 */ 282 */
236 private StickyAxisAnnotation calcWAnnotation(final ResultRow row) { 283 private StickyAxisAnnotation calcInfrastructureWAnnotation(final ResultRow row) {
237 final String label = Resources.getMsg(this.context.getMeta(), "sinfo.chart.flood_duration.curve.infrastructure", 284 final String label = Resources.getMsg(this.context.getMeta(), "sinfo.chart.flood_duration.curve.infrastructure",
238 "sinfo.chart.flood_duration.curve.infrastructure", 285 "sinfo.chart.flood_duration.curve.infrastructure",
239 SInfoResultType.infrastructuretype.exportValue(this.context, row.getValue(SInfoResultType.infrastructuretype)) 286 SInfoResultType.infrastructuretype.exportValue(this.context, row.getValue(SInfoResultType.infrastructuretype))
240 + ", " + SInfoResultType.riverside.exportValue(this.context, row.getValue(SInfoResultType.riverside))); 287 + ", " + SInfoResultType.riverside.exportValue(this.context, row.getValue(SInfoResultType.riverside)));
241 final StickyAxisAnnotation annotation = new StickyAxisAnnotation(label, (float) row.getDoubleValue(SInfoResultType.infrastructureHeight), 288 final StickyAxisAnnotation annotation = new StickyAxisAnnotation(label, (float) row.getDoubleValue(SInfoResultType.infrastructureHeight),
376 row.putValue(SInfoResultType.customMultiRowColWaterlevel, waterlevels); 423 row.putValue(SInfoResultType.customMultiRowColWaterlevel, waterlevels);
377 424
378 return row; 425 return row;
379 } 426 }
380 427
428 /// **
429 // * Calculate the result row fields for one infrastructure - gives wrong duration numbers where Q changes within the
430 /// gauge range
431 // */
432 // private void calculateInfrastructure(final ResultRow row, final Gauge gauge, final InfrastructureValue
433 /// infrastructure, final WQBaseTableFinder wqFinder,
434 // final Map<Gauge, GaugeDurationValuesFinder> durFinders) {
435 //
436 // final double q = wqFinder.getDischarge(infrastructure.getStation(), infrastructure.getHeight());
437 // final double qOut = Double.isInfinite(q) ? Double.NaN : q;
438 // final double dur = underflowDaysToOverflowDays(durFinders.get(gauge).getDuration(q));
439 // row.putValue(SInfoResultType.riverside, infrastructure.getAttributeKey());
440 // row.putValue(SInfoResultType.floodDuration, dur);
441 // row.putValue(SInfoResultType.floodDischarge, qOut);
442 // row.putValue(SInfoResultType.infrastructureHeight, infrastructure.getHeight());
443 // row.putValue(SInfoResultType.infrastructuretype, infrastructure.getInfrastructure().getType().getName());
444 // }
445
381 /** 446 /**
382 * Calculate the result row fields for one infrastructure 447 * Calculate the result row fields for one infrastructure
383 */ 448 */
384 private void calculateInfrastructure(final ResultRow row, final Gauge gauge, final InfrastructureValue infrastructure, final WQBaseTableFinder wqFinder, 449 private void calculateInfrastructure(final ResultRow row, final Gauge gauge, final InfrastructureValue infrastructure, final WstValueTable wst,
385 final Map<Gauge, GaugeDurationValuesFinder> durFinders) { 450 final Map<Gauge, GaugeDurationValuesFinder> durFinders) {
386 451
387 final double q = wqFinder.getDischarge(infrastructure.getStation(), infrastructure.getHeight()); 452 // Interpolate the infrastructure height in the wst table to get the corresponding Q
453 final Calculation problems = new Calculation();
454 final double[] qs = wst.findQsForW(infrastructure.getStation().doubleValue(), infrastructure.getHeight().doubleValue(), problems);
455 // TODO Fehlerbehandlung (kein Q gefunden)
456 final double q = (qs.length >= 1) ? qs[0] : Double.NaN;
388 final double qOut = Double.isInfinite(q) ? Double.NaN : q; 457 final double qOut = Double.isInfinite(q) ? Double.NaN : q;
389 final double dur = underflowDaysToOverflowDays(durFinders.get(gauge).getDuration(q)); 458 // Determine the relative column position of the Q
459 final QPosition qPos = wst.getQPosition(infrastructure.getStation().doubleValue(), q);
460 // Get the Q for the found column position for the station of the gauge
461 final double qGauge = wst.getQ(qPos, gauge.getStation().doubleValue());
462 // Interpolate the Q-D-table of the gauge
463 final double dur = underflowDaysToOverflowDays(durFinders.get(gauge).getDuration(qGauge));
464 // Set result row
390 row.putValue(SInfoResultType.riverside, infrastructure.getAttributeKey()); 465 row.putValue(SInfoResultType.riverside, infrastructure.getAttributeKey());
391 row.putValue(SInfoResultType.floodDuration, dur); 466 row.putValue(SInfoResultType.floodDuration, dur);
392 row.putValue(SInfoResultType.floodDischarge, qOut); 467 row.putValue(SInfoResultType.floodDischarge, qOut);
393 row.putValue(SInfoResultType.infrastructureHeight, infrastructure.getHeight()); 468 row.putValue(SInfoResultType.infrastructureHeight, infrastructure.getHeight());
394 row.putValue(SInfoResultType.infrastructuretype, infrastructure.getInfrastructure().getType().getName()); 469 row.putValue(SInfoResultType.infrastructuretype, infrastructure.getInfrastructure().getType().getName());

http://dive4elements.wald.intevation.org