Mercurial > dive4elements > river
comparison artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flood_duration/FloodDurationCalculator.java @ 9202:b4402594213b
More work on calculations and output for S-Info flood duration workflow (chart types 1 and 2)
author | mschaefer |
---|---|
date | Mon, 02 Jul 2018 07:33:53 +0200 |
parents | 1614cb14308f |
children | 3dae6b78e1da |
comparison
equal
deleted
inserted
replaced
9201:491e1a434457 | 9202:b4402594213b |
---|---|
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; | 13 import java.util.Collection; |
14 import java.util.HashMap; | 14 import java.util.HashMap; |
15 import java.util.List; | |
15 import java.util.Map; | 16 import java.util.Map; |
17 import java.util.Set; | |
16 | 18 |
17 import org.apache.commons.lang.math.DoubleRange; | 19 import org.apache.commons.lang.math.DoubleRange; |
18 import org.dive4elements.artifacts.CallContext; | 20 import org.dive4elements.artifacts.CallContext; |
21 import org.dive4elements.river.artifacts.WINFOArtifact; | |
22 import org.dive4elements.river.artifacts.access.ComputationRangeAccess; | |
19 import org.dive4elements.river.artifacts.common.GeneralResultType; | 23 import org.dive4elements.river.artifacts.common.GeneralResultType; |
20 import org.dive4elements.river.artifacts.common.ResultRow; | 24 import org.dive4elements.river.artifacts.common.ResultRow; |
21 import org.dive4elements.river.artifacts.model.Calculation; | 25 import org.dive4elements.river.artifacts.model.Calculation; |
26 import org.dive4elements.river.artifacts.model.Calculation.Problem; | |
27 import org.dive4elements.river.artifacts.model.CalculationResult; | |
28 import org.dive4elements.river.artifacts.model.WQKms; | |
22 import org.dive4elements.river.artifacts.sinfo.common.GaugeDurationValuesFinder; | 29 import org.dive4elements.river.artifacts.sinfo.common.GaugeDurationValuesFinder; |
30 import org.dive4elements.river.artifacts.sinfo.common.GaugeMainValueFinder; | |
23 import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider; | 31 import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider; |
24 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType; | 32 import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType; |
25 import org.dive4elements.river.artifacts.sinfo.common.WQBaseTableFinder; | 33 import org.dive4elements.river.artifacts.sinfo.common.WQBaseTableFinder; |
26 import org.dive4elements.river.artifacts.sinfo.flood_duration.RiversideRadioChoice.RiversideChoiceKey; | 34 import org.dive4elements.river.artifacts.sinfo.flood_duration.RiversideRadioChoice.RiversideChoiceKey; |
27 import org.dive4elements.river.artifacts.sinfo.util.RiverInfo; | |
28 import org.dive4elements.river.model.Attribute.AttributeKey; | 35 import org.dive4elements.river.model.Attribute.AttributeKey; |
29 import org.dive4elements.river.model.Gauge; | 36 import org.dive4elements.river.model.Gauge; |
37 import org.dive4elements.river.model.MainValueType.MainValueTypeKey; | |
30 import org.dive4elements.river.model.sinfo.InfrastructureValue; | 38 import org.dive4elements.river.model.sinfo.InfrastructureValue; |
39 | |
40 import gnu.trove.TDoubleArrayList; | |
31 | 41 |
32 /** | 42 /** |
33 * Calculation of the result rows of the flood duration of the infrastructures in a river km range | 43 * Calculation of the result rows of the flood duration of the infrastructures in a river km range |
34 * and selected main value durations | 44 * and selected main value durations |
35 * | 45 * |
48 this.context = context; | 58 this.context = context; |
49 this.riverInfoProvider = riverInfoProvider; | 59 this.riverInfoProvider = riverInfoProvider; |
50 } | 60 } |
51 | 61 |
52 /** | 62 /** |
53 * Calculate the result rows | 63 * Calculate the infrastructures flood duration result rows |
54 * | 64 */ |
55 * @return a result collection with one result | 65 public FloodDurationCalculationResult execute(final Calculation problems, final String label, final DoubleRange calcRange, |
56 */ | 66 final RiversideChoiceKey riverside, final WINFOArtifact winfo) { |
57 public FloodDurationCalculationResults execute(final Calculation problems, final String label, final String calcModeLabel, | 67 |
58 final DoubleRange calcRange, final RiversideChoiceKey riverside, final String user) { | 68 // Find all gauges of the calc range, and create the duration finders |
59 | 69 final Map<Gauge, GaugeDurationValuesFinder> durFinders = new HashMap<>(); |
70 Gauge firstGauge = null; | |
71 for (final Gauge gauge : this.riverInfoProvider.getRiver().determineGauges(calcRange.getMinimumDouble(), calcRange.getMaximumDouble())) { | |
72 durFinders.put(gauge, GaugeDurationValuesFinder.loadValues(gauge, problems)); | |
73 if (firstGauge == null) | |
74 firstGauge = gauge; | |
75 } | |
76 | |
77 // Find all infrastructures within the calc range | |
78 final AttributeKey bankKey = riverside.getAttributeKey(); | |
79 final List<InfrastructureValue> infras = InfrastructureValue.getValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(), | |
80 calcRange.getMaximumDouble(), bankKey); | |
81 | |
82 // Merge all stations (range/step, borders of gauge ranges, infrastructures) | |
83 final Map<Double, InfrastructureValue> allStations = new HashMap<>(); | |
84 final Map<Double, InfrastructureValue> secondBank = new HashMap<>(); // any second infrastructure in case of both-banks-option | |
85 addRangeStations(allStations, winfo); | |
86 addGaugeLimits(allStations, durFinders.keySet(), calcRange.getMinimumDouble(), calcRange.getMaximumDouble()); | |
87 addInfrastructures(allStations, secondBank, infras); | |
88 final double[] stationsSorted = sortStations(allStations.keySet()); | |
89 | |
90 // Calculate W and Q for all stations and the selected discharge states | |
91 final WQKms[] wqkmsArray = calculateWaterlevels(winfo, stationsSorted, problems); | |
92 | |
93 // Determine discharge state labels of the main values | |
94 final String[] mainValueLabels = findMainValueLabels(wqkmsArray, winfo.getQs(), firstGauge, problems); | |
95 | |
96 // Create a finder for Q in the {river}.wst km-w-q table | |
60 final WQBaseTableFinder wqFinder = WQBaseTableFinder.loadValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(), | 97 final WQBaseTableFinder wqFinder = WQBaseTableFinder.loadValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(), |
61 calcRange.getMaximumDouble(), problems); | 98 calcRange.getMaximumDouble(), problems); |
62 final Map<Gauge, GaugeDurationValuesFinder> durFinders = new HashMap<>(); | 99 |
63 for (final Gauge gauge : this.riverInfoProvider.getRiver().determineGauges(calcRange.getMinimumDouble(), calcRange.getMaximumDouble())) { | 100 // Calculate the durations and create the result rows |
64 durFinders.put(gauge, GaugeDurationValuesFinder.loadValues(gauge, problems)); | 101 for (int i = 0; i <= stationsSorted.length - 1; i++) { |
65 } | 102 final Gauge gauge = this.riverInfoProvider.getGauge(stationsSorted[i], true); |
66 final AttributeKey bankKey = riverside.getAttributeKey(); | 103 final ResultRow row = createRow(stationsSorted[i], gauge, wqkmsArray, durFinders.get(gauge), i); |
67 for (final InfrastructureValue infrastructure : InfrastructureValue.getValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(), | 104 if (allStations.containsKey(stationsSorted[i]) && (allStations.get(stationsSorted[i]) != null)) |
68 calcRange.getMaximumDouble(), bankKey)) { | 105 calculateInfrastructure(row, gauge, allStations.get(stationsSorted[i]), wqFinder, durFinders); |
69 calculateResultRow(infrastructure, wqFinder, durFinders); | 106 this.rows.add(row); |
70 } | 107 if (secondBank.containsKey(stationsSorted[i])) { |
71 | 108 final ResultRow row2 = ResultRow.create(row); |
72 final FloodDurationCalculationResult result = new FloodDurationCalculationResult(label, this.rows); | 109 calculateInfrastructure(row2, gauge, secondBank.get(stationsSorted[i]), wqFinder, durFinders); |
73 | 110 this.rows.add(row2); |
74 final RiverInfo riverInfo = new RiverInfo(this.riverInfoProvider.getRiver()); | 111 } |
75 | 112 } |
76 final FloodDurationCalculationResults results = new FloodDurationCalculationResults(calcModeLabel, user, riverInfo, calcRange); | 113 |
77 results.addResult(result, problems); | 114 return new FloodDurationCalculationResult(label, mainValueLabels, this.rows); |
78 return results; | 115 } |
79 } | 116 |
80 | 117 /** |
81 /** | 118 * Adds to a stations map all stations corresponding to the active range and step |
82 * Calculate the result row for one infrastructure | 119 */ |
83 */ | 120 private void addRangeStations(final Map<Double, InfrastructureValue> allStations, final WINFOArtifact winfo) { |
84 private void calculateResultRow(final InfrastructureValue infrastructure, final WQBaseTableFinder wqFinder, | 121 for (final double station : new ComputationRangeAccess(winfo).getKms()) |
85 final Map<Gauge, GaugeDurationValuesFinder> durFinders) { | 122 allStations.put(Double.valueOf(station), null); |
123 } | |
124 | |
125 /** | |
126 * Adds to a stations map all range limits of the gauges within the calc range | |
127 */ | |
128 private void addGaugeLimits(final Map<Double, InfrastructureValue> allStations, final Set<Gauge> gauges, final double fromKm, final double toKm) { | |
129 for (final Gauge gauge : gauges) { | |
130 final Double kmA = Double.valueOf(gauge.getRange().getA().doubleValue()); | |
131 final Double kmB = Double.valueOf(gauge.getRange().getB().doubleValue()); | |
132 if (kmA > fromKm - 0.0001) | |
133 allStations.put(kmA, null); | |
134 if (kmB < toKm + 0.0001) | |
135 allStations.put(kmB, null); | |
136 } | |
137 } | |
138 | |
139 /** | |
140 * Adds to a stations map all (first) infrastructures of a station, and the second, if any, to another map | |
141 */ | |
142 private void addInfrastructures(final Map<Double, InfrastructureValue> allStations, final Map<Double, InfrastructureValue> secondBank, | |
143 final List<InfrastructureValue> infrastructures) { | |
144 for (final InfrastructureValue infrastructure : infrastructures) { | |
145 final Double station = infrastructure.getStation(); | |
146 if (!allStations.containsKey(station) || !(allStations.get(station) instanceof InfrastructureValue)) | |
147 allStations.put(station, infrastructure); | |
148 else | |
149 secondBank.put(station, infrastructure); | |
150 } | |
151 } | |
152 | |
153 /** | |
154 * Returns a double array with a sorted stations set | |
155 */ | |
156 private double[] sortStations(final Set<Double> stations) { | |
157 final TDoubleArrayList sorted = new TDoubleArrayList(); | |
158 for (final Double station : stations) | |
159 sorted.add(station.doubleValue()); | |
160 sorted.sort(); | |
161 return sorted.toNativeArray(); | |
162 } | |
163 | |
164 /** | |
165 * Calculates an array of w-q-longitudinal sections for all artifact W/Q options | |
166 */ | |
167 private WQKms[] calculateWaterlevels(final WINFOArtifact winfo, final double[] stations, final Calculation problems) { | |
168 // REMARK aus TkhCalculation - move to WinfoArtifactWrapper? | |
169 // TODO das ist ziemlich langsam - durch den WQBaseTableFinder ersetzen? (vorher W-Optionen in Q umrechnen) | |
170 // (So funktioniert computeWaterlevelData wohl: | |
171 // Es sucht die Spalte(n) zum Bezugspegel-Q in der W-Q-Tabelle ({river}.wst in Wst etc.) | |
172 // und interpoliert für diese horizontale Tabellenposition jeweils die vertikale Tabellenposition der station; | |
173 // das ergibt das W einer station für einen Abflusszustand; | |
174 // bei Vorgabe eines Pegel-W wird vorher anhand der W-Q-Tabelle des Pegels ({gauge}.at in DischargeTable) das Q | |
175 // interpoliert; | |
176 // bei Vorgabe eines W auf freier Strecke wird wohl vorher noch die .wst-Interpolation eingesetzt. | |
177 final CalculationResult waterlevelData = winfo.computeWaterlevelData(stations); | |
178 | |
179 /* copy all problems */ | |
180 final Calculation winfoProblems = waterlevelData.getReport(); | |
181 final List<Problem> problems2 = winfoProblems.getProblems(); | |
182 if (problems2 != null) { | |
183 for (final Problem problem : problems2) { | |
184 problems.addProblem(problem); | |
185 } | |
186 } | |
187 return (WQKms[]) waterlevelData.getData(); | |
188 } | |
189 | |
190 /** | |
191 * Determines the discharge state labels for the selected Q or W values | |
192 */ | |
193 private String[] findMainValueLabels(final WQKms[] wqkmsArray, final double[] qs, final Gauge gauge, final Calculation problems) { | |
194 final String[] mainValueLabels = new String[wqkmsArray.length]; | |
195 if (wqkmsArray.length >= 1) { | |
196 // Labels like Q=123 or W=123 | |
197 for (int i = 0; i <= wqkmsArray.length - 1; i++) | |
198 mainValueLabels[i] = wqkmsArray[i].getName(); | |
199 // Replace labels for named main Q values | |
200 final GaugeMainValueFinder zoneFinder = GaugeMainValueFinder.loadValues(MainValueTypeKey.Q, gauge, problems); | |
201 if ((zoneFinder != null) && (qs != null)) { | |
202 for (int i = 0; i <= qs.length - 1; i++) | |
203 mainValueLabels[i] = zoneFinder.findExactZoneName(qs[i], mainValueLabels[i]); | |
204 } | |
205 } | |
206 return mainValueLabels; | |
207 } | |
208 | |
209 /** | |
210 * Create a result row for a station and its gauge, and add w-q-values as selected | |
211 */ | |
212 private ResultRow createRow(final Double station, final Gauge gauge, final WQKms[] wqkmsArray, | |
213 final GaugeDurationValuesFinder durationFinder, final int kmIndex) { | |
86 | 214 |
87 final ResultRow row = ResultRow.create(); | 215 final ResultRow row = ResultRow.create(); |
88 | 216 row.putValue(GeneralResultType.station, station); |
89 final Gauge gauge = this.riverInfoProvider.getGauge(infrastructure.getStation(), true); | 217 row.putValue(SInfoResultType.infrastructuretype, null); // is replaced later for an infrastructure |
218 row.putValue(SInfoResultType.floodDuration, Double.NaN); // is replaced later for an infrastructure | |
219 row.putValue(SInfoResultType.gaugeLabel, gauge.getName()); | |
220 final String location = this.riverInfoProvider.getLocation(station); | |
221 row.putValue(SInfoResultType.location, location); | |
222 | |
223 if (wqkmsArray.length >= 1) { | |
224 assert (wqkmsArray[0].getKm(kmIndex) == station.doubleValue()); | |
225 row.putValue(SInfoResultType.waterlevel1, wqkmsArray[0].getW(kmIndex)); | |
226 row.putValue(SInfoResultType.discharge1, wqkmsArray[0].getQ(kmIndex)); | |
227 row.putValue(SInfoResultType.mainValue1Duration, underflowDaysToOverflowDays(durationFinder.getDuration(wqkmsArray[0].getQ(kmIndex)))); | |
228 if (wqkmsArray.length >= 2) { | |
229 assert (wqkmsArray[1].getKm(kmIndex) == station.doubleValue()); | |
230 row.putValue(SInfoResultType.waterlevel2, wqkmsArray[1].getW(kmIndex)); | |
231 row.putValue(SInfoResultType.discharge2, wqkmsArray[1].getQ(kmIndex)); | |
232 row.putValue(SInfoResultType.mainValue2Duration, underflowDaysToOverflowDays(durationFinder.getDuration(wqkmsArray[1].getQ(kmIndex)))); | |
233 if (wqkmsArray.length >= 3) { | |
234 assert (wqkmsArray[2].getKm(kmIndex) == station.doubleValue()); | |
235 row.putValue(SInfoResultType.waterlevel3, wqkmsArray[2].getW(kmIndex)); | |
236 row.putValue(SInfoResultType.discharge3, wqkmsArray[2].getQ(kmIndex)); | |
237 row.putValue(SInfoResultType.mainValue3Duration, underflowDaysToOverflowDays(durationFinder.getDuration(wqkmsArray[2].getQ(kmIndex)))); | |
238 } | |
239 } | |
240 } | |
241 return row; | |
242 } | |
243 | |
244 /** | |
245 * Calculate the result row fields for one infrastructure | |
246 */ | |
247 private void calculateInfrastructure(final ResultRow row, final Gauge gauge, final InfrastructureValue infrastructure, | |
248 final WQBaseTableFinder wqFinder, final Map<Gauge, GaugeDurationValuesFinder> durFinders) { | |
249 | |
90 final double q = wqFinder.getDischarge(infrastructure.getStation(), infrastructure.getHeight()); | 250 final double q = wqFinder.getDischarge(infrastructure.getStation(), infrastructure.getHeight()); |
91 final double qOut = Double.isInfinite(q) ? Double.NaN : q; | 251 final double qOut = Double.isInfinite(q) ? Double.NaN : q; |
92 // REMARK: access the location once only during calculation | 252 final double dur = underflowDaysToOverflowDays(durFinders.get(gauge).getDuration(q)); |
93 final String location = this.riverInfoProvider.getLocation(infrastructure.getStation()); | |
94 row.putValue(GeneralResultType.station, infrastructure.getStation()); | |
95 row.putValue(SInfoResultType.riverside, infrastructure.getAttributeKey().getName()); // TODO i18n | 253 row.putValue(SInfoResultType.riverside, infrastructure.getAttributeKey().getName()); // TODO i18n |
96 row.putValue(SInfoResultType.floodDuration, 365 - durFinders.get(gauge).getDuration(q)); | 254 row.putValue(SInfoResultType.floodDuration, dur); |
97 row.putValue(SInfoResultType.floodDischarge, qOut); | 255 row.putValue(SInfoResultType.floodDischarge, qOut); |
98 row.putValue(SInfoResultType.infrastructureHeight, infrastructure.getHeight()); | 256 row.putValue(SInfoResultType.infrastructureHeight, infrastructure.getHeight()); |
99 row.putValue(SInfoResultType.infrastructuretype, infrastructure.getInfrastructure().getType().getName()); | 257 row.putValue(SInfoResultType.infrastructuretype, infrastructure.getInfrastructure().getType().getName()); |
100 | 258 } |
101 // TODO Berechne W, Überflutungsdauer, Q und Bezeichnung von bis zu drei WSPL | 259 |
102 // row.putValue(SInfoResultType.customMultiRowColWaterlevel, rowWsps); | 260 /** |
103 | 261 * Translates underflow duration into overflow duration |
104 row.putValue(SInfoResultType.gaugeLabel, gauge.getName()); | 262 */ |
105 row.putValue(SInfoResultType.location, location); | 263 private double underflowDaysToOverflowDays(final double underflowDays) { |
106 | 264 return 365 - underflowDays; |
107 this.rows.add(row); | |
108 } | 265 } |
109 } | 266 } |