Mercurial > dive4elements > river
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; |