Mercurial > dive4elements > river
comparison artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthCalculation.java @ 8883:a536e1aacf0f
Further work on SINFO-FlowDepth
author | gernotbelger |
---|---|
date | Fri, 09 Feb 2018 18:07:22 +0100 |
parents | f762fadc5313 |
children | 7a8c12706834 |
comparison
equal
deleted
inserted
replaced
8882:f762fadc5313 | 8883:a536e1aacf0f |
---|---|
7 * and comes with ABSOLUTELY NO WARRANTY! Check out the | 7 * and comes with ABSOLUTELY NO WARRANTY! Check out the |
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.flowdepth; | 10 package org.dive4elements.river.artifacts.sinfo.flowdepth; |
11 | 11 |
12 import java.util.ArrayList; | |
12 import java.util.Collection; | 13 import java.util.Collection; |
14 import java.util.Collections; | |
13 import java.util.List; | 15 import java.util.List; |
14 | 16 |
17 import org.apache.commons.math.FunctionEvaluationException; | |
18 import org.apache.commons.math.analysis.UnivariateRealFunction; | |
15 import org.dive4elements.artifacts.ArtifactDatabase; | 19 import org.dive4elements.artifacts.ArtifactDatabase; |
16 import org.dive4elements.artifacts.CallContext; | 20 import org.dive4elements.artifacts.CallContext; |
17 import org.dive4elements.river.artifacts.BedHeightsArtifact; | 21 import org.dive4elements.river.artifacts.BedHeightsArtifact; |
18 import org.dive4elements.river.artifacts.model.Calculation; | 22 import org.dive4elements.river.artifacts.model.Calculation; |
19 import org.dive4elements.river.artifacts.model.CalculationResult; | 23 import org.dive4elements.river.artifacts.model.CalculationResult; |
24 import org.dive4elements.river.artifacts.sinfo.SINFOArtifact; | 28 import org.dive4elements.river.artifacts.sinfo.SINFOArtifact; |
25 import org.dive4elements.river.artifacts.sinfo.flowdepth.FlowDepthAccess.DifferencesPair; | 29 import org.dive4elements.river.artifacts.sinfo.flowdepth.FlowDepthAccess.DifferencesPair; |
26 import org.dive4elements.river.artifacts.states.WaterlevelData; | 30 import org.dive4elements.river.artifacts.states.WaterlevelData; |
27 import org.dive4elements.river.artifacts.states.WaterlevelFetcher; | 31 import org.dive4elements.river.artifacts.states.WaterlevelFetcher; |
28 import org.dive4elements.river.model.BedHeight; | 32 import org.dive4elements.river.model.BedHeight; |
33 import org.dive4elements.river.model.BedHeightValue; | |
29 import org.dive4elements.river.model.Gauge; | 34 import org.dive4elements.river.model.Gauge; |
30 import org.dive4elements.river.model.River; | 35 import org.dive4elements.river.model.River; |
36 import org.dive4elements.river.utils.DoubleUtil; | |
31 import org.dive4elements.river.utils.GaugeIndex; | 37 import org.dive4elements.river.utils.GaugeIndex; |
32 import org.dive4elements.river.utils.RiverUtils; | 38 import org.dive4elements.river.utils.RiverUtils; |
33 | 39 |
34 class FlowDepthCalculation { | 40 class FlowDepthCalculation { |
35 | 41 |
105 problems.addProblem(message); | 111 problems.addProblem(message); |
106 return null; | 112 return null; |
107 } | 113 } |
108 final WKms wstKms = waterlevel.getWkms(); | 114 final WKms wstKms = waterlevel.getWkms(); |
109 | 115 |
116 final String wspLabel = wstKms.getName(); | |
117 final String soundingLabel = bedHeight.getDescription(); | |
118 final String label = String.format("%s - %s", wspLabel, soundingLabel); | |
119 | |
120 checkYearDifference(label, waterlevel, bedHeight, problems); | |
110 checkWaterlevelDiscretisation(wstKms, problems); | 121 checkWaterlevelDiscretisation(wstKms, problems); |
111 | |
112 // FIXME: woher bekommen? | |
113 final int wspYear = 0; | |
114 | 122 |
115 /* re-determine the reference gauge, in the same way as the WaterlevelArtifact would do it */ | 123 /* re-determine the reference gauge, in the same way as the WaterlevelArtifact would do it */ |
116 final String notinrange = Resources.getMsg(this.context.getMeta(), CSV_NOT_IN_GAUGE_RANGE, | 124 final String notinrange = Resources.getMsg(this.context.getMeta(), CSV_NOT_IN_GAUGE_RANGE, |
117 CSV_NOT_IN_GAUGE_RANGE); | 125 CSV_NOT_IN_GAUGE_RANGE); |
118 | 126 |
119 final Gauge refGauge = waterlevel.findReferenceGauge(river); | 127 final Gauge refGauge = waterlevel.findReferenceGauge(river); |
120 final String refGaugeName = refGauge == null ? notinrange : refGauge.getName(); | 128 final String refGaugeName = refGauge == null ? notinrange : refGauge.getName(); |
121 | 129 |
122 final String wspLabel = wstKms.getName(); | |
123 final String soundingLabel = bedHeight.getDescription(); | |
124 final String label = String.format("%s - %s", wspLabel, soundingLabel); | |
125 | |
126 final BedHeightInfo sounding = BedHeightInfo.from(bedHeight); | 130 final BedHeightInfo sounding = BedHeightInfo.from(bedHeight); |
131 final int wspYear = waterlevel.getYear(); | |
127 final WstInfo wstInfo = new WstInfo(wspLabel, wspYear, refGaugeName); | 132 final WstInfo wstInfo = new WstInfo(wspLabel, wspYear, refGaugeName); |
128 | 133 |
129 final FlowDepthCalculationResult resultData = new FlowDepthCalculationResult(label, wstInfo, sounding); | 134 final FlowDepthCalculationResult resultData = new FlowDepthCalculationResult(label, wstInfo, sounding); |
130 | 135 |
131 // TODO: Berechnung der Transportkörperhöhen | 136 // FIXME: nur prüfen/beschaffen wenn TKH Berechnung aktiv |
132 // - woher kommen die zusätzlichen eingangsdaten? sind das fixe daten pro gewässer? --> falls ja, warum nicht | 137 /* Abflusswerte vorhanden? */ |
133 // einmal berechnen und in db ablegen? | |
134 | |
135 // Benötigte Daten | |
136 // - Abfluss / Station | |
137 // - kein Abfluss --> Fehler | |
138 if (!(wstKms instanceof QKms)) { | 138 if (!(wstKms instanceof QKms)) { |
139 final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingQ", | 139 final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingQ", |
140 null, label); | 140 null, label); |
141 problems.addProblem(message); | 141 problems.addProblem(message); |
142 // TODO: keine Berechnung TKH | 142 // TODO: keine Berechnung TKH |
143 } | 143 } |
144 | 144 |
145 // - Sohlbeschaffenheit (D50 Korndurchmesser aus Seddb) | 145 // - Sohlbeschaffenheit (D50 Korndurchmesser aus Seddb) |
146 // - Abhängig von Peiljahr | 146 // - Abhängig von Peiljahr |
147 // - kein D50 vorhanden --> Fehler | 147 // - kein D50 vorhanden --> Fehler |
148 | |
148 // - Art der Gewässersohle (starr/mobil) | 149 // - Art der Gewässersohle (starr/mobil) |
149 | 150 |
150 final String bedHeightLabel = bedHeight.getDescription(); | 151 final String bedHeightLabel = bedHeight.getDescription(); |
151 final String wstLabel = wstKms.getName(); | 152 final String wstLabel = wstKms.getName(); |
152 | 153 |
153 // FIXME: basis der diskretisierung ist bedHeight, die wspl werden interpoliert | 154 final UnivariateRealFunction wstInterpolator = DoubleUtil.getLinearInterpolator(wstKms.allKms(), |
154 for (int i = 0; i < wstKms.size(); i++) { | 155 wstKms.allWs()); |
155 | 156 |
156 final double km = wstKms.getKm(i); | 157 // FIXME: sort by station first, but in what direction? |
157 final double wst = wstKms.getW(i); | 158 final List<BedHeightValue> values = bedHeight.getValues(); |
158 // FIXME: interpolate from bedheights? | 159 |
159 final double meanBedHeight = 79.32; | 160 final List<BedHeightValue> sortedValues = new ArrayList<>(values); |
160 | 161 Collections.sort(sortedValues, new BedHeightStationComparator()); |
161 final double flowDepth = wst - meanBedHeight; | 162 |
162 | 163 for (final BedHeightValue bedHeightValue : sortedValues) { |
163 final double discharge = wstKms instanceof QKms ? ((QKms) wstKms).getQ(i) : Double.NaN; | 164 |
164 | 165 final Double station = bedHeightValue.getStation(); |
165 // FIXME: calculate tkh | 166 if (station == null || station.isNaN()) |
166 final double tkh = 0; | 167 continue; |
167 final double flowDepthTkh = flowDepth - tkh; | 168 |
168 | 169 final Double meanBedHeightDbl = bedHeightValue.getHeight(); |
169 // REMARK: access the location once only during calculation | 170 if (meanBedHeightDbl == null || meanBedHeightDbl.isNaN()) |
170 final String location = LocationProvider.getLocation(river.getName(), km); | 171 continue; |
171 | 172 |
172 // REMARK: access the gauge once only during calculation | 173 final double km = station; |
173 final Gauge gauge = findGauge(waterlevel, refGauge, gaugeIndex, km); | 174 final double meanBedHeight = meanBedHeightDbl; |
174 | 175 |
175 final String gaugeLabel = gauge == null ? notinrange : gauge.getName(); | 176 try { |
176 | 177 // FIXME: check out of range |
177 resultData.addRow(km, flowDepth, flowDepthTkh, tkh, wst, discharge, wstLabel, gaugeLabel, meanBedHeight, | 178 final double wst = wstInterpolator.value(km); |
178 bedHeightLabel, location); | 179 |
180 final double flowDepth = wst - meanBedHeight; | |
181 | |
182 // FIXME: piecewise constant interpolation? | |
183 // final double discharge = wstKms instanceof QKms ? ((QKms) wstKms).getQ(i) : Double.NaN; | |
184 final double discharge = Double.NaN; | |
185 | |
186 // FIXME: calculate tkh | |
187 final double tkh = 0; | |
188 final double flowDepthTkh = flowDepth - tkh; | |
189 | |
190 // REMARK: access the location once only during calculation | |
191 final String location = LocationProvider.getLocation(river.getName(), km); | |
192 | |
193 // REMARK: access the gauge once only during calculation | |
194 final Gauge gauge = findGauge(waterlevel, refGauge, gaugeIndex, km); | |
195 | |
196 final String gaugeLabel = gauge == null ? notinrange : gauge.getName(); | |
197 | |
198 resultData.addRow(km, flowDepth, flowDepthTkh, tkh, wst, discharge, wstLabel, gaugeLabel, meanBedHeight, | |
199 bedHeightLabel, location); | |
200 } | |
201 catch (final FunctionEvaluationException e) { | |
202 /* should only happen if out of range */ | |
203 e.printStackTrace(); | |
204 /* simply ignore */ | |
205 } | |
206 | |
179 } | 207 } |
180 | 208 |
181 return resultData; | 209 return resultData; |
210 } | |
211 | |
212 /** | |
213 * Checks the year difference between waterlevels and sounding, and issues a warning if too big. | |
214 * | |
215 * Zeitraum Zeitliche Differenz [a] | |
216 * X ≥ 1998 ± 3 | |
217 * 1958 ≤ X < 1998 ± 6 | |
218 * 1918 ≤ X < 1958 ± 12 | |
219 * X < 1918 ± 25 | |
220 */ | |
221 private void checkYearDifference(final String label, final WaterlevelData waterlevel, final BedHeight sounding, | |
222 final Calculation problems) { | |
223 | |
224 final Integer soundingYear = sounding.getYear(); | |
225 if (soundingYear == null) | |
226 return; | |
227 | |
228 final int wstYear = waterlevel.getYear(); | |
229 if (wstYear < 0) | |
230 return; | |
231 | |
232 final int maxDifference = getMaxDifferenceYears(soundingYear); | |
233 | |
234 final int difference = Math.abs(soundingYear - wstYear); | |
235 if (difference > maxDifference) { | |
236 final String message = Resources.getMsg(this.context.getMeta(), | |
237 "sinfo_calc_flow_depth.warning.year_difference", null, label, difference); | |
238 problems.addProblem(message); | |
239 } | |
240 } | |
241 | |
242 private int getMaxDifferenceYears(final int year) { | |
243 | |
244 if (year < 1918) | |
245 return 25; | |
246 | |
247 if (1918 <= year && year < 1958) | |
248 return 12; | |
249 | |
250 if (1958 <= year && year < 1998) | |
251 return 6; | |
252 | |
253 /* >= 1998 */ | |
254 return 3; | |
182 } | 255 } |
183 | 256 |
184 private Gauge findGauge(final WaterlevelData waterlevel, final Gauge refGauge, final GaugeIndex gaugeIndex, | 257 private Gauge findGauge(final WaterlevelData waterlevel, final Gauge refGauge, final GaugeIndex gaugeIndex, |
185 final double km) { | 258 final double km) { |
186 | 259 |
214 } | 287 } |
215 } | 288 } |
216 | 289 |
217 private BedHeight loadBedHeight(final String soundingId, final double from, final double to) { | 290 private BedHeight loadBedHeight(final String soundingId, final double from, final double to) { |
218 | 291 |
219 // FIXME: absolutely unbelievable.... | 292 // REMARK: absolutely unbelievable.... |
220 // The way how bed-heights (and other data too) is accessed is different for nearly ever calculation-type | 293 // The way how bed-heights (and other data too) is accessed is different for nearly ever calculation-type |
221 // throughout flys. | 294 // throughout flys. |
222 // The knowledge on how to parse the datacage-ids is spread through the complete code-base... | 295 // The knowledge on how to parse the datacage-ids is spread through the complete code-base... |
223 | 296 |
224 // We use here the way on how bed-heights are accessed by the BedDifferenceAccess/BedDifferenceCalculation, but | 297 // We use here the way on how bed-heights are accessed by the BedDifferenceAccess/BedDifferenceCalculation, but |
226 final String[] parts = soundingId.split(";"); | 299 final String[] parts = soundingId.split(";"); |
227 | 300 |
228 final BedHeightsArtifact artifact = (BedHeightsArtifact) RiverUtils.getArtifact(parts[0], this.context); | 301 final BedHeightsArtifact artifact = (BedHeightsArtifact) RiverUtils.getArtifact(parts[0], this.context); |
229 | 302 |
230 final Integer bedheightId = artifact.getDataAsInteger("height_id"); | 303 final Integer bedheightId = artifact.getDataAsInteger("height_id"); |
231 // FIXME: this only works with type 'single'; unclear on how to distinguish from epoch data (or whatever the | 304 // REMARK: this only works with type 'single'; unclear on how to distinguish from epoch data (or whatever the |
232 // other type means) | 305 // other type means) |
233 // Luckily, the requirement is to only access 'single' data here. | 306 // Luckily, the requirement is to only access 'single' data here. |
234 // final String bedheightType = artifact.getDataAsString("type"); | 307 // final String bedheightType = artifact.getDataAsString("type"); |
235 | 308 |
236 // FIXME: BedDifferences uses this, but we also need the metadata of the BedHeight | 309 // REMARK: BedDifferences uses this, but we also need the metadata of the BedHeight |
237 // FIXME: second absolutely awful thing: BedHeight is a hibernate binding class, accessing the database via | 310 // REMARK: second absolutely awful thing: BedHeight is a hibernate binding class, accessing the database via |
238 // hibernate stuff | 311 // hibernate stuff |
239 // BedHeightFactory uses its own (direct) way of accessing the data, with its own implemented data classes. | 312 // BedHeightFactory uses its own (direct) way of accessing the data, with its own implemented data classes. |
240 // return BedHeightFactory.getHeight(bedheightType, bedheightId, from, to); | 313 // return BedHeightFactory.getHeight(bedheightType, bedheightId, from, to); |
241 | 314 |
242 return BedHeight.getBedHeightById(bedheightId); | 315 return BedHeight.getBedHeightById(bedheightId); |