comparison artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculator.java @ 9259:66b003701546

Added infrastructure height and Q annotation to S-Info flood duration curve chart
author mschaefer
date Mon, 16 Jul 2018 08:43:07 +0200
parents ef7b65576d4b
children e5367900dd6d
comparison
equal deleted inserted replaced
9258:d950c6055102 9259:66b003701546
8 * documentation coming with Dive4Elements River for details. 8 * documentation coming with Dive4Elements River for details.
9 */ 9 */
10 package org.dive4elements.river.artifacts.sinfo.flood_duration; 10 package org.dive4elements.river.artifacts.sinfo.flood_duration;
11 11
12 import java.util.ArrayList; 12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.HashMap; 13 import java.util.HashMap;
15 import java.util.List; 14 import java.util.List;
16 import java.util.Map; 15 import java.util.Map;
17 import java.util.Set; 16 import java.util.Set;
18 17
25 import org.dive4elements.river.artifacts.model.Calculation; 24 import org.dive4elements.river.artifacts.model.Calculation;
26 import org.dive4elements.river.artifacts.model.Calculation.Problem; 25 import org.dive4elements.river.artifacts.model.Calculation.Problem;
27 import org.dive4elements.river.artifacts.model.CalculationResult; 26 import org.dive4elements.river.artifacts.model.CalculationResult;
28 import org.dive4elements.river.artifacts.model.WQDay; 27 import org.dive4elements.river.artifacts.model.WQDay;
29 import org.dive4elements.river.artifacts.model.WQKms; 28 import org.dive4elements.river.artifacts.model.WQKms;
29 import org.dive4elements.river.artifacts.resources.Resources;
30 import org.dive4elements.river.artifacts.sinfo.common.GaugeDurationValuesFinder; 30 import org.dive4elements.river.artifacts.sinfo.common.GaugeDurationValuesFinder;
31 import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider; 31 import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider;
32 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType; 32 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType;
33 import org.dive4elements.river.artifacts.sinfo.common.WQBaseTableFinder; 33 import org.dive4elements.river.artifacts.sinfo.common.WQBaseTableFinder;
34 import org.dive4elements.river.artifacts.sinfo.flood_duration.RiversideRadioChoice.RiversideChoiceKey; 34 import org.dive4elements.river.artifacts.sinfo.flood_duration.RiversideRadioChoice.RiversideChoiceKey;
35 import org.dive4elements.river.exports.WaterlevelDescriptionBuilder; 35 import org.dive4elements.river.exports.WaterlevelDescriptionBuilder;
36 import org.dive4elements.river.jfree.RiverAnnotation;
37 import org.dive4elements.river.jfree.StickyAxisAnnotation; 36 import org.dive4elements.river.jfree.StickyAxisAnnotation;
38 import org.dive4elements.river.jfree.StickyAxisAnnotation.SimpleAxis; 37 import org.dive4elements.river.jfree.StickyAxisAnnotation.SimpleAxis;
39 import org.dive4elements.river.model.Attribute.AttributeKey; 38 import org.dive4elements.river.model.Attribute.AttributeKey;
40 import org.dive4elements.river.model.Gauge; 39 import org.dive4elements.river.model.Gauge;
41 import org.dive4elements.river.model.sinfo.InfrastructureValue; 40 import org.dive4elements.river.model.sinfo.InfrastructureValue;
48 * 47 *
49 * @author Matthias Schäfer 48 * @author Matthias Schäfer
50 */ 49 */
51 final class FloodDurationCalculator { 50 final class FloodDurationCalculator {
52 51
53 private final Collection<ResultRow> rows = new ArrayList<>(); 52 private final List<ResultRow> rows = new ArrayList<>();
54 53
55 private final RiverInfoProvider riverInfoProvider; 54 private final RiverInfoProvider riverInfoProvider;
56 55
57 private final CallContext context; 56 private final CallContext context;
58 57
93 // TODO Laut Herrn Reiß: Q und D jeweils konstant für jedes Pegel-Intervall, Q-Änderungen (Zuflüsse etc.) aus .wst 92 // TODO Laut Herrn Reiß: Q und D jeweils konstant für jedes Pegel-Intervall, Q-Änderungen (Zuflüsse etc.) aus .wst
94 // ignorieren 93 // ignorieren
95 final WQKms[] wqkmsArray = calculateWaterlevels(winfo, stationsSorted, problems); 94 final WQKms[] wqkmsArray = calculateWaterlevels(winfo, stationsSorted, problems);
96 95
97 // Determine discharge state labels of the main values 96 // Determine discharge state labels of the main values
98 final WaterlevelDescriptionBuilder wdescBuilder = new WaterlevelDescriptionBuilder(winfo, this.context); 97 updateMainValueLabels(wqkmsArray, winfo, problems);
99 final String[] mainValueLabels = findMainValueLabels(wqkmsArray, winfo.getQs(), wdescBuilder, problems);
100 98
101 // Create a finder for Q in the {river}.wst km-w-q table 99 // Create a finder for Q in the {river}.wst km-w-q table
102 final WQBaseTableFinder wqFinder = WQBaseTableFinder.loadValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(), 100 final WQBaseTableFinder wqFinder = WQBaseTableFinder.loadValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(),
103 calcRange.getMaximumDouble(), problems); 101 calcRange.getMaximumDouble(), problems);
104 102
105 final WaterlevelDescriptionBuilder descBuilder = new WaterlevelDescriptionBuilder(winfo, this.context);
106
107 // Calculate the durations and create the result rows 103 // Calculate the durations and create the result rows
108 for (int i = 0; i <= stationsSorted.length - 1; i++) { 104 for (int i = 0; i <= stationsSorted.length - 1; i++) {
109 final Gauge gauge = this.riverInfoProvider.getGauge(stationsSorted[i], true); 105 final Gauge gauge = this.riverInfoProvider.getGauge(stationsSorted[i], true);
110 final ResultRow row = createRow(descBuilder, stationsSorted[i], gauge, firstGauge, wqkmsArray, durFinders.get(gauge), i); 106 final ResultRow row = createRow(stationsSorted[i], gauge, firstGauge, wqkmsArray, durFinders.get(gauge), i);
111 if (allStations.containsKey(stationsSorted[i]) && (allStations.get(stationsSorted[i]) != null)) 107 if (allStations.containsKey(stationsSorted[i]) && (allStations.get(stationsSorted[i]) != null))
112 calculateInfrastructure(row, gauge, allStations.get(stationsSorted[i]), wqFinder, durFinders); 108 calculateInfrastructure(row, gauge, allStations.get(stationsSorted[i]), wqFinder, durFinders);
113 this.rows.add(row); 109 this.rows.add(row);
114 if (secondBank.containsKey(stationsSorted[i])) { 110 if (secondBank.containsKey(stationsSorted[i])) {
115 final ResultRow row2 = ResultRow.create(row); 111 final ResultRow row2 = ResultRow.create(row);
116 calculateInfrastructure(row2, gauge, secondBank.get(stationsSorted[i]), wqFinder, durFinders); 112 calculateInfrastructure(row2, gauge, secondBank.get(stationsSorted[i]), wqFinder, durFinders);
117 this.rows.add(row2); 113 this.rows.add(row2);
118 } 114 }
119 } 115 }
116 final String[] mainValueLabels = new String[wqkmsArray.length];
117 for (int i = 0; i <= wqkmsArray.length - 1; i++)
118 mainValueLabels[i] = wqkmsArray[i].getName();
120 results.addResult(new FloodDurationCalculationResult(label, mainValueLabels, this.rows), problems); 119 results.addResult(new FloodDurationCalculationResult(label, mainValueLabels, this.rows), problems);
120
121 calcWQDays(problems, stationsSorted[0], AttributeKey.LEFT, winfo, results); 121 calcWQDays(problems, stationsSorted[0], AttributeKey.LEFT, winfo, results);
122 calcMainValueAnnotations(label, problems, 0, AttributeKey.LEFT, wqkmsArray, mainValueLabels, results); 122
123 // TODO Infrastrukturhoehe 123 calcMainValueAnnotations(problems, 0, AttributeKey.LEFT, wqkmsArray, results);
124
125 calcInfrastructureAnnotations(problems, this.rows.get(0), wqkmsArray, results);
124 } 126 }
125 127
126 /** 128 /**
127 * Calculate duration curve for a station and add to result collection 129 * Calculate duration curve for a station and add to result collection
128 */ 130 */
147 } 149 }
148 150
149 /** 151 /**
150 * Calculate the data for the W and Q main value lines in the duration curve chart and add to result collection 152 * Calculate the data for the W and Q main value lines in the duration curve chart and add to result collection
151 */ 153 */
152 private void calcMainValueAnnotations(final String label, final Calculation problems, final int stationIndex, final AttributeKey riverside, 154 private void calcMainValueAnnotations(final Calculation problems, final int stationIndex, final AttributeKey riverside,
153 final WQKms[] wqkmsArray, final String[] mainValueLabels, final FloodDurationCalculationResults results) { 155 final WQKms[] wqkmsArray, final FloodDurationCalculationResults results) {
154 156
155 // Same way as in MainValueWFacet and ..QFacet 157 // Same way as in MainValueWFacet and ..QFacet, but special label handling
156 final List<StickyAxisAnnotation> ws = new ArrayList<>(); 158 final List<StickyAxisAnnotation> ws = new ArrayList<>();
157 final List<StickyAxisAnnotation> qs = new ArrayList<>(); 159 final List<StickyAxisAnnotation> qs = new ArrayList<>();
158 for (int i = 0; i <= wqkmsArray.length - 1; i++) { 160 for (int i = 0; i <= wqkmsArray.length - 1; i++) {
159 final StickyAxisAnnotation qAnnotation = new StickyAxisAnnotation(mainValueLabels[i], (float) wqkmsArray[i].getQ(stationIndex), SimpleAxis.Y_AXIS, 161 final String qLabel = wqkmsArray[i].getName().startsWith("W=") ? "Q(" + wqkmsArray[i].getName() + ")" : wqkmsArray[i].getName();
162 final StickyAxisAnnotation qAnnotation = new StickyAxisAnnotation(qLabel, (float) wqkmsArray[i].getQ(stationIndex), SimpleAxis.Y_AXIS,
160 FloodDurationCurveGenerator.YAXIS.Q.idx); 163 FloodDurationCurveGenerator.YAXIS.Q.idx);
161 qs.add(qAnnotation); 164 qs.add(qAnnotation);
162 setHitPoint((WQDay) results.getDurationCurve().getData(), qAnnotation); 165 FloodDurationMainValuesQFacet.setHitPoint((WQDay) results.getDurationCurve().getData(), qAnnotation);
163 final StickyAxisAnnotation wAnnotation = new StickyAxisAnnotation(mainValueLabels[i], (float) wqkmsArray[i].getW(stationIndex), SimpleAxis.Y_AXIS, 166 final String wLabel = !wqkmsArray[i].getName().startsWith("W=") ? "W(" + wqkmsArray[i].getName() + ")" : wqkmsArray[i].getName();
167 final StickyAxisAnnotation wAnnotation = new StickyAxisAnnotation(wLabel, (float) wqkmsArray[i].getW(stationIndex), SimpleAxis.Y_AXIS,
164 FloodDurationCurveGenerator.YAXIS.W.idx); 168 FloodDurationCurveGenerator.YAXIS.W.idx);
165 ws.add(wAnnotation); 169 ws.add(wAnnotation);
166 setHitPoint((WQDay) results.getDurationCurve().getData(), wAnnotation); 170 FloodDurationMainValuesWFacet.setHitPoint((WQDay) results.getDurationCurve().getData(), wAnnotation);
167 } 171 }
168 // TODO RiverAnnotation ersetzen weil das eine NotSerializableException erzeugt 172 results.getMainValueQAnnotations().clear();
169 results.setMainValueQAnnotation(new RiverAnnotation(label, qs)); 173 results.getMainValueQAnnotations().addAll(qs);
170 results.setMainValueWAnnotation(new RiverAnnotation(label, ws)); 174 results.getMainValueWAnnotations().clear();
171 } 175 results.getMainValueWAnnotations().addAll(ws);
172 176 }
173 /** 177
174 * Set the hit-point in Q where a line drawn from the axis would hit the 178 /**
175 * curve in WQDay (if hit). 179 * Calculate the data for the W and Q lines in the duration curve chart for the infrastructure height and add to result
176 * Employ linear interpolation. 180 * collection
177 */ 181 */
178 private static void setHitPoint(final WQDay wqday, final StickyAxisAnnotation annotation) { 182 private void calcInfrastructureAnnotations(final Calculation problems, final ResultRow row, final WQKms[] wqkmsArray,
179 183 final FloodDurationCalculationResults results) {
180 final float q = annotation.getPos(); 184
181 final Double day = wqday.interpolateDayByQ(q); 185 if (row.getValue(SInfoResultType.infrastructuretype) == null) {
182 if (day != null) { 186 results.setInfrastructureWAnnotation(null);
183 annotation.setHitPoint(day.floatValue()); 187 results.setInfrastructureQAnnotation(null);
184 } 188 return;
185 // else if (log.isDebugEnabled()) { 189 }
186 // log.debug("StickyAnnotation does not hit wqday curve: " + q); 190 // Same way as in MainValueWFacet and ..QFacet
187 // } 191 final String label = Resources.getMsg(this.context.getMeta(), "sinfo.chart.flood_duration.curve.infrastructure",
192 "sinfo.chart.flood_duration.curve.infrastructure",
193 SInfoResultType.infrastructuretype.exportValue(this.context, row.getValue(SInfoResultType.infrastructuretype))
194 + ", " + SInfoResultType.riverside.exportValue(this.context, row.getValue(SInfoResultType.riverside)));
195 final StickyAxisAnnotation qAnnotation = new StickyAxisAnnotation(label, (float) row.getDoubleValue(SInfoResultType.floodDischarge),
196 SimpleAxis.Y_AXIS, FloodDurationCurveGenerator.YAXIS.Q.idx);
197 FloodDurationMainValuesQFacet.setHitPoint((WQDay) results.getDurationCurve().getData(), qAnnotation);
198 final StickyAxisAnnotation wAnnotation = new StickyAxisAnnotation(label, (float) row.getDoubleValue(SInfoResultType.infrastructureHeight),
199 SimpleAxis.Y_AXIS, FloodDurationCurveGenerator.YAXIS.W.idx);
200 FloodDurationMainValuesWFacet.setHitPoint((WQDay) results.getDurationCurve().getData(), wAnnotation);
201
202 results.setInfrastructureQAnnotation(qAnnotation);
203 results.setInfrastructureWAnnotation(wAnnotation);
188 } 204 }
189 205
190 /** 206 /**
191 * Adds to a stations map all stations corresponding to the active range and step 207 * Adds to a stations map all stations corresponding to the active range and step
192 */ 208 */
260 } 276 }
261 return (WQKms[]) waterlevelData.getData(); 277 return (WQKms[]) waterlevelData.getData();
262 } 278 }
263 279
264 /** 280 /**
265 * Determines the waterlevel/discharge state labels for the selected Q or W values 281 * Determines the waterlevel/discharge state labels for the selected Q or W values and sets them in the WQKms array
266 */ 282 */
267 private String[] findMainValueLabels(final WQKms[] wqkmsArray, final double[] qs, final WaterlevelDescriptionBuilder descBuilder, 283 private void updateMainValueLabels(final WQKms[] wqkmsArray, final WINFOArtifact winfo, final Calculation problems) {
268 final Calculation problems) { 284
269
270 final String[] mainValueLabels = new String[wqkmsArray.length];
271 for (int i = 0; i <= wqkmsArray.length - 1; i++) 285 for (int i = 0; i <= wqkmsArray.length - 1; i++)
272 mainValueLabels[i] = descBuilder.getDesc(wqkmsArray[i]); 286 wqkmsArray[i].setName(buildWQDescription(wqkmsArray[i], winfo));
273 return mainValueLabels; 287 }
288
289 /**
290 *
291 * @param wqkms
292 * @param descBuilder
293 * @return
294 */
295 private String buildWQDescription(final WQKms wqkms, final WINFOArtifact winfo) {
296 final WaterlevelDescriptionBuilder descBuilder = new WaterlevelDescriptionBuilder(winfo, this.context);
297 final String description = descBuilder.getDesc(wqkms);
298 if (!description.isEmpty() && Character.isDigit(description.charAt(0))) {
299 if (winfo.isQ())
300 return "Q=" + description;
301 else
302 return "W=" + description;
303 }
304 else
305 return description;
274 } 306 }
275 307
276 /** 308 /**
277 * Create a result row for a station and its gauge, and add w-q-values as selected 309 * Create a result row for a station and its gauge, and add w-q-values as selected
278 * 310 */
279 * @param descBuilder 311 private ResultRow createRow(final Double station, final Gauge gauge, final Gauge firstGauge, final WQKms[] wqkmsArray,
280 */ 312 final GaugeDurationValuesFinder durationFinder, final int kmIndex) {
281 private ResultRow createRow(final WaterlevelDescriptionBuilder descBuilder, final Double station, final Gauge gauge, final Gauge firstGauge,
282 final WQKms[] wqkmsArray, final GaugeDurationValuesFinder durationFinder, final int kmIndex) {
283 313
284 final ResultRow row = ResultRow.create(); 314 final ResultRow row = ResultRow.create();
285 row.putValue(GeneralResultType.station, station); 315 row.putValue(GeneralResultType.station, station);
286 row.putValue(SInfoResultType.infrastructuretype, null); // is replaced later for an infrastructure 316 row.putValue(SInfoResultType.infrastructuretype, null); // is replaced later for an infrastructure
287 row.putValue(SInfoResultType.floodDuration, Double.NaN); // is replaced later for an infrastructure 317 row.putValue(SInfoResultType.floodDuration, Double.NaN); // is replaced later for an infrastructure
292 final String location = this.riverInfoProvider.getLocation(station); 322 final String location = this.riverInfoProvider.getLocation(station);
293 row.putValue(SInfoResultType.location, location); 323 row.putValue(SInfoResultType.location, location);
294 324
295 final List<DurationWaterlevel> waterlevels = new ArrayList<>(wqkmsArray.length); 325 final List<DurationWaterlevel> waterlevels = new ArrayList<>(wqkmsArray.length);
296 326
297 for (final WQKms wqKm : wqkmsArray) { 327 for (final WQKms wqKms : wqkmsArray) {
298 assert (wqKm.getKm(kmIndex) == station.doubleValue()); 328 assert (wqKms.getKm(kmIndex) == station.doubleValue());
299 329
300 final int overflowDays = (int) Math.round(underflowDaysToOverflowDays(durationFinder.getDuration(wqKm.getQ(kmIndex)))); 330 final int overflowDays = (int) Math.round(underflowDaysToOverflowDays(durationFinder.getDuration(wqKms.getQ(kmIndex))));
301 331
302 final String waterlevelLabel = descBuilder.getDesc(wqKm); 332 final DurationWaterlevel dw = new DurationWaterlevel(wqKms.getW(kmIndex), overflowDays, wqKms.getQ(kmIndex), wqKms.getName());
303
304 final DurationWaterlevel dw = new DurationWaterlevel(wqKm.getW(kmIndex), overflowDays, wqKm.getQ(kmIndex), waterlevelLabel);
305 waterlevels.add(dw); 333 waterlevels.add(dw);
306 } 334 }
307 row.putValue(SInfoResultType.customMultiRowColWaterlevel, waterlevels); 335 row.putValue(SInfoResultType.customMultiRowColWaterlevel, waterlevels);
308 336
309 return row; 337 return row;

http://dive4elements.wald.intevation.org