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);

http://dive4elements.wald.intevation.org