# HG changeset patch # User gernotbelger # Date 1518196042 -3600 # Node ID a536e1aacf0f6e2823561b4b705ecb211eada515 # Parent f762fadc5313ec4d61f6f08a1533e8c45f3856b1 Further work on SINFO-FlowDepth diff -r f762fadc5313 -r a536e1aacf0f artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixRealizingCalculationExtended.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixRealizingCalculationExtended.java Fri Feb 09 18:07:22 2018 +0100 @@ -0,0 +1,45 @@ +/** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde + * Software engineering by + * Björnsen Beratende Ingenieure GmbH + * Dr. Schumacher Ingenieurbüro für Wasser und Umwelt + * + * This file is Free Software under the GNU AGPL (>=v3) + * and comes with ABSOLUTELY NO WARRANTY! Check out the + * documentation coming with Dive4Elements River for details. + */ +package org.dive4elements.river.artifacts.model.fixings; + +import java.util.Date; +import java.util.List; + +import org.dive4elements.river.artifacts.access.FixRealizingAccess; +import org.dive4elements.river.artifacts.model.FixingsOverview; +import org.dive4elements.river.artifacts.model.FixingsOverviewFactory; + +/** + * REMARK: this inheritance is only needed, beause changing the orignal calculation will probably break the + * serialization of the artifact.... + * + * @author Gernot Belger + */ +public class FixRealizingCalculationExtended extends FixRealizingCalculation { + private static final long serialVersionUID = 1L; + + public FixRealizingCalculationExtended(final FixRealizingAccess fixAccess) { + super(fixAccess); + } + + // FIXME: implement + // FIXME: check if this breaks serialization + public int determineMeanYear() { + final FixingsOverview overview = FixingsOverviewFactory.getOverview(this.river); + final ColumnCache cc = new ColumnCache(); + + final List columns = getEventColumns(overview, cc); + for (final Column column : columns) { + final Date date = column.getDate(); + } + + return 1999; + } +} diff -r f762fadc5313 -r a536e1aacf0f artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/BedHeightStationComparator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/BedHeightStationComparator.java Fri Feb 09 18:07:22 2018 +0100 @@ -0,0 +1,27 @@ +/** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde + * Software engineering by + * Björnsen Beratende Ingenieure GmbH + * Dr. Schumacher Ingenieurbüro für Wasser und Umwelt + * + * This file is Free Software under the GNU AGPL (>=v3) + * and comes with ABSOLUTELY NO WARRANTY! Check out the + * documentation coming with Dive4Elements River for details. + */ +package org.dive4elements.river.artifacts.sinfo.flowdepth; + +import java.util.Comparator; + +import org.dive4elements.river.model.BedHeightValue; + +/** + * @author Gernot Belger + */ +public class BedHeightStationComparator implements Comparator { + + @Override + public int compare(final BedHeightValue o1, final BedHeightValue o2) { + final Double s1 = o1.getStation(); + final Double s2 = o2.getStation(); + return Double.compare(s1, s2); + } +} \ No newline at end of file diff -r f762fadc5313 -r a536e1aacf0f artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthCalculation.java --- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthCalculation.java Fri Feb 09 16:11:47 2018 +0100 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthCalculation.java Fri Feb 09 18:07:22 2018 +0100 @@ -9,9 +9,13 @@ */ package org.dive4elements.river.artifacts.sinfo.flowdepth; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; +import org.apache.commons.math.FunctionEvaluationException; +import org.apache.commons.math.analysis.UnivariateRealFunction; import org.dive4elements.artifacts.ArtifactDatabase; import org.dive4elements.artifacts.CallContext; import org.dive4elements.river.artifacts.BedHeightsArtifact; @@ -26,8 +30,10 @@ import org.dive4elements.river.artifacts.states.WaterlevelData; import org.dive4elements.river.artifacts.states.WaterlevelFetcher; import org.dive4elements.river.model.BedHeight; +import org.dive4elements.river.model.BedHeightValue; import org.dive4elements.river.model.Gauge; import org.dive4elements.river.model.River; +import org.dive4elements.river.utils.DoubleUtil; import org.dive4elements.river.utils.GaugeIndex; import org.dive4elements.river.utils.RiverUtils; @@ -107,10 +113,12 @@ } final WKms wstKms = waterlevel.getWkms(); - checkWaterlevelDiscretisation(wstKms, problems); + final String wspLabel = wstKms.getName(); + final String soundingLabel = bedHeight.getDescription(); + final String label = String.format("%s - %s", wspLabel, soundingLabel); - // FIXME: woher bekommen? - final int wspYear = 0; + checkYearDifference(label, waterlevel, bedHeight, problems); + checkWaterlevelDiscretisation(wstKms, problems); /* re-determine the reference gauge, in the same way as the WaterlevelArtifact would do it */ final String notinrange = Resources.getMsg(this.context.getMeta(), CSV_NOT_IN_GAUGE_RANGE, @@ -119,22 +127,14 @@ final Gauge refGauge = waterlevel.findReferenceGauge(river); final String refGaugeName = refGauge == null ? notinrange : refGauge.getName(); - final String wspLabel = wstKms.getName(); - final String soundingLabel = bedHeight.getDescription(); - final String label = String.format("%s - %s", wspLabel, soundingLabel); - final BedHeightInfo sounding = BedHeightInfo.from(bedHeight); + final int wspYear = waterlevel.getYear(); final WstInfo wstInfo = new WstInfo(wspLabel, wspYear, refGaugeName); final FlowDepthCalculationResult resultData = new FlowDepthCalculationResult(label, wstInfo, sounding); - // TODO: Berechnung der Transportkörperhöhen - // - woher kommen die zusätzlichen eingangsdaten? sind das fixe daten pro gewässer? --> falls ja, warum nicht - // einmal berechnen und in db ablegen? - - // Benötigte Daten - // - Abfluss / Station - // - kein Abfluss --> Fehler + // FIXME: nur prüfen/beschaffen wenn TKH Berechnung aktiv + /* Abflusswerte vorhanden? */ if (!(wstKms instanceof QKms)) { final String message = Resources.getMsg(this.context.getMeta(), "sinfo_calc_flow_depth.warning.missingQ", null, label); @@ -145,42 +145,115 @@ // - Sohlbeschaffenheit (D50 Korndurchmesser aus Seddb) // - Abhängig von Peiljahr // - kein D50 vorhanden --> Fehler + // - Art der Gewässersohle (starr/mobil) final String bedHeightLabel = bedHeight.getDescription(); final String wstLabel = wstKms.getName(); - // FIXME: basis der diskretisierung ist bedHeight, die wspl werden interpoliert - for (int i = 0; i < wstKms.size(); i++) { - - final double km = wstKms.getKm(i); - final double wst = wstKms.getW(i); - // FIXME: interpolate from bedheights? - final double meanBedHeight = 79.32; - - final double flowDepth = wst - meanBedHeight; - - final double discharge = wstKms instanceof QKms ? ((QKms) wstKms).getQ(i) : Double.NaN; + final UnivariateRealFunction wstInterpolator = DoubleUtil.getLinearInterpolator(wstKms.allKms(), + wstKms.allWs()); - // FIXME: calculate tkh - final double tkh = 0; - final double flowDepthTkh = flowDepth - tkh; - - // REMARK: access the location once only during calculation - final String location = LocationProvider.getLocation(river.getName(), km); + // FIXME: sort by station first, but in what direction? + final List values = bedHeight.getValues(); - // REMARK: access the gauge once only during calculation - final Gauge gauge = findGauge(waterlevel, refGauge, gaugeIndex, km); + final List sortedValues = new ArrayList<>(values); + Collections.sort(sortedValues, new BedHeightStationComparator()); - final String gaugeLabel = gauge == null ? notinrange : gauge.getName(); + for (final BedHeightValue bedHeightValue : sortedValues) { - resultData.addRow(km, flowDepth, flowDepthTkh, tkh, wst, discharge, wstLabel, gaugeLabel, meanBedHeight, - bedHeightLabel, location); + final Double station = bedHeightValue.getStation(); + if (station == null || station.isNaN()) + continue; + + final Double meanBedHeightDbl = bedHeightValue.getHeight(); + if (meanBedHeightDbl == null || meanBedHeightDbl.isNaN()) + continue; + + final double km = station; + final double meanBedHeight = meanBedHeightDbl; + + try { + // FIXME: check out of range + final double wst = wstInterpolator.value(km); + + final double flowDepth = wst - meanBedHeight; + + // FIXME: piecewise constant interpolation? + // final double discharge = wstKms instanceof QKms ? ((QKms) wstKms).getQ(i) : Double.NaN; + final double discharge = Double.NaN; + + // FIXME: calculate tkh + final double tkh = 0; + final double flowDepthTkh = flowDepth - tkh; + + // REMARK: access the location once only during calculation + final String location = LocationProvider.getLocation(river.getName(), km); + + // REMARK: access the gauge once only during calculation + final Gauge gauge = findGauge(waterlevel, refGauge, gaugeIndex, km); + + final String gaugeLabel = gauge == null ? notinrange : gauge.getName(); + + resultData.addRow(km, flowDepth, flowDepthTkh, tkh, wst, discharge, wstLabel, gaugeLabel, meanBedHeight, + bedHeightLabel, location); + } + catch (final FunctionEvaluationException e) { + /* should only happen if out of range */ + e.printStackTrace(); + /* simply ignore */ + } + } return resultData; } + /** + * Checks the year difference between waterlevels and sounding, and issues a warning if too big. + * + * Zeitraum Zeitliche Differenz [a] + * X ≥ 1998 ± 3 + * 1958 ≤ X < 1998 ± 6 + * 1918 ≤ X < 1958 ± 12 + * X < 1918 ± 25 + */ + private void checkYearDifference(final String label, final WaterlevelData waterlevel, final BedHeight sounding, + final Calculation problems) { + + final Integer soundingYear = sounding.getYear(); + if (soundingYear == null) + return; + + final int wstYear = waterlevel.getYear(); + if (wstYear < 0) + return; + + final int maxDifference = getMaxDifferenceYears(soundingYear); + + final int difference = Math.abs(soundingYear - wstYear); + if (difference > maxDifference) { + final String message = Resources.getMsg(this.context.getMeta(), + "sinfo_calc_flow_depth.warning.year_difference", null, label, difference); + problems.addProblem(message); + } + } + + private int getMaxDifferenceYears(final int year) { + + if (year < 1918) + return 25; + + if (1918 <= year && year < 1958) + return 12; + + if (1958 <= year && year < 1998) + return 6; + + /* >= 1998 */ + return 3; + } + private Gauge findGauge(final WaterlevelData waterlevel, final Gauge refGauge, final GaugeIndex gaugeIndex, final double km) { @@ -216,7 +289,7 @@ private BedHeight loadBedHeight(final String soundingId, final double from, final double to) { - // FIXME: absolutely unbelievable.... + // REMARK: absolutely unbelievable.... // The way how bed-heights (and other data too) is accessed is different for nearly ever calculation-type // throughout flys. // The knowledge on how to parse the datacage-ids is spread through the complete code-base... @@ -228,17 +301,17 @@ final BedHeightsArtifact artifact = (BedHeightsArtifact) RiverUtils.getArtifact(parts[0], this.context); final Integer bedheightId = artifact.getDataAsInteger("height_id"); - // FIXME: this only works with type 'single'; unclear on how to distinguish from epoch data (or whatever the + // REMARK: this only works with type 'single'; unclear on how to distinguish from epoch data (or whatever the // other type means) // Luckily, the requirement is to only access 'single' data here. // final String bedheightType = artifact.getDataAsString("type"); - // FIXME: BedDifferences uses this, but we also need the metadata of the BedHeight - // FIXME: second absolutely awful thing: BedHeight is a hibernate binding class, accessing the database via + // REMARK: BedDifferences uses this, but we also need the metadata of the BedHeight + // REMARK: second absolutely awful thing: BedHeight is a hibernate binding class, accessing the database via // hibernate stuff // BedHeightFactory uses its own (direct) way of accessing the data, with its own implemented data classes. // return BedHeightFactory.getHeight(bedheightType, bedheightId, from, to); return BedHeight.getBedHeightById(bedheightId); } -} \ No newline at end of file +} diff -r f762fadc5313 -r a536e1aacf0f artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelData.java --- a/artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelData.java Fri Feb 09 16:11:47 2018 +0100 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelData.java Fri Feb 09 18:07:22 2018 +0100 @@ -23,20 +23,23 @@ private final String name; + private final int year; + /** If true, tabular export will show gauges for every station, else only for the first gauge */ private final boolean showAllGauges; - public WaterlevelData(final WKms wkms) { - this(wkms, false); + public WaterlevelData(final WKms wkms, final int year) { + this(wkms, year, false); } - public WaterlevelData(final WKms wkms, final boolean showAllGauges) { - this(null, wkms, showAllGauges); + public WaterlevelData(final WKms wkms, final int year, final boolean showAllGauges) { + this(null, wkms, year, showAllGauges); } - public WaterlevelData(final String name, final WKms wkms, final boolean showAllGauges) { + private WaterlevelData(final String name, final WKms wkms, final int year, final boolean showAllGauges) { this.name = name; this.wkms = wkms; + this.year = year; this.showAllGauges = showAllGauges; } @@ -46,11 +49,11 @@ } final WKms filteredWkms = this.wkms.filteredKms(from, to); - return new WaterlevelData(filteredWkms); + return new WaterlevelData(this.name, filteredWkms, this.year, this.showAllGauges); } public WaterlevelData withName(final String nameToSet) { - return new WaterlevelData(nameToSet, this.wkms, this.showAllGauges); + return new WaterlevelData(nameToSet, this.wkms, this.year, this.showAllGauges); } public String getName() { @@ -81,4 +84,8 @@ return new double[] { from, to }; } + + public int getYear() { + return this.year; + } } diff -r f762fadc5313 -r a536e1aacf0f artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelFetcher.java --- a/artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelFetcher.java Fri Feb 09 16:11:47 2018 +0100 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelFetcher.java Fri Feb 09 18:07:22 2018 +0100 @@ -23,6 +23,7 @@ import org.dive4elements.river.artifacts.model.Segment; import org.dive4elements.river.artifacts.model.WKms; import org.dive4elements.river.artifacts.model.WQKms; +import org.dive4elements.river.artifacts.model.fixings.FixRealizingCalculationExtended; import org.dive4elements.river.artifacts.model.fixings.FixRealizingResult; import org.dive4elements.river.artifacts.states.DefaultState.ComputeType; import org.dive4elements.river.utils.RiverUtils; @@ -86,7 +87,11 @@ final WKms wkms = staticWKms.getWKms(idx, from, to); if (wkms != null) - return new WaterlevelData(wkms); + { + // FIXME: woher bekommen?: eventuell zusammenhang mit tabelle 'time_intervals' + final int year = -1; + return new WaterlevelData(wkms, year); + } log.error("No WKms from Static artifact for this range."); return null; @@ -100,7 +105,11 @@ final WQKms wkms = staticWKms.getWQKms(from, to); if (wkms != null) - return new WaterlevelData(wkms); + { + // FIXME: woher bekommen?: eventuell zusammenhang mit tabelle 'time_intervals' + final int year = -1; + return new WaterlevelData(wkms, year); + } log.error("No WKms from Static artifact for this range."); return null; @@ -122,7 +131,9 @@ return null; } - return new WaterlevelData(wkms[idx]).filterByRange(from, to); + // REAMRK: W_INFO results does not know any 'year' + final int year = -1; + return new WaterlevelData(wkms[idx], year).filterByRange(from, to); } private WaterlevelData fetchFixationArtifactWaterlevel(final CallContext context, @@ -138,9 +149,13 @@ final List segments = fixAccess.getSegments(); final boolean isFixRealize = (segments != null && !segments.isEmpty()); + /* ugly but necessary to keep this logic at least a bit inside the FixRealizing stuff */ + final FixRealizingCalculationExtended calculation = new FixRealizingCalculationExtended(fixAccess); + final int year = calculation.determineMeanYear(); + // REMARK: same logic as in WaterlevelExporter final boolean showAllGauges = isFixRealize; - return new WaterlevelData(frR.getWQKms()[idx], showAllGauges).filterByRange(from, to); + return new WaterlevelData(frR.getWQKms()[idx], year, showAllGauges).filterByRange(from, to); } -} +} \ No newline at end of file diff -r f762fadc5313 -r a536e1aacf0f artifacts/src/main/resources/messages.properties --- a/artifacts/src/main/resources/messages.properties Fri Feb 09 16:11:47 2018 +0100 +++ b/artifacts/src/main/resources/messages.properties Fri Feb 09 18:07:22 2018 +0100 @@ -772,6 +772,7 @@ sinfo_calc_flow_depth=Flie\u00dftiefen sinfo_calc_flow_depth.warning.missingQ = {0}: keine Abflussdaten vorhanden, Transportk\u00f6rperh\u00f6henberechnung nicht m\u00f6glich sinfo_calc_flow_depth.warning.waterlevel_discretisation = Wasserspiegel {0}: r\u00e4umliche Aufl\u00f6sung betr\u00e4gt mehr als 1000m +sinfo_calc_flow_depth.warning.year_difference = {0}: Zeitliche Abweichung betr\u00e4gt {1} Jahre. Dies kann zu unplausiblen Ergebnissen f\u00fchren sinfo_calc_flow_depth_development=Flie\u00dftiefenentwicklung sinfo_calc_flow_depth_minmax=Minimale und Maximale Flie\u00dftiefe diff -r f762fadc5313 -r a536e1aacf0f artifacts/src/main/resources/messages_de.properties --- a/artifacts/src/main/resources/messages_de.properties Fri Feb 09 16:11:47 2018 +0100 +++ b/artifacts/src/main/resources/messages_de.properties Fri Feb 09 18:07:22 2018 +0100 @@ -778,6 +778,7 @@ sinfo_calc_flow_depth=Flie\u00dftiefen sinfo_calc_flow_depth.warning.missingQ = {0}: keine Abflussdaten vorhanden, Transportk\u00f6rperh\u00f6henberechnung nicht m\u00f6glich sinfo_calc_flow_depth.warning.waterlevel_discretisation = Wasserspiegel {0}: r\u00e4umliche Aufl\u00f6sung betr\u00e4gt mehr als 1000m +sinfo_calc_flow_depth.warning.year_difference = {0}: Zeitliche Abweichung betr\u00e4gt {1} Jahre. Dies kann zu unplausiblen Ergebnissen f\u00fchren sinfo_calc_flow_depth_development=Flie\u00dftiefenentwicklung sinfo_calc_flow_depth_minmax=Minimale und Maximale Flie\u00dftiefe diff -r f762fadc5313 -r a536e1aacf0f artifacts/src/main/resources/messages_de_DE.properties --- a/artifacts/src/main/resources/messages_de_DE.properties Fri Feb 09 16:11:47 2018 +0100 +++ b/artifacts/src/main/resources/messages_de_DE.properties Fri Feb 09 18:07:22 2018 +0100 @@ -774,6 +774,7 @@ sinfo_calc_flow_depth=Flie\u00dftiefen sinfo_calc_flow_depth.warning.missingQ = {0}: keine Abflussdaten vorhanden, Transportk\u00f6rperh\u00f6henberechnung nicht m\u00f6glich sinfo_calc_flow_depth.warning.waterlevel_discretisation = Wasserspiegel {0}: r\u00e4umliche Aufl\u00f6sung betr\u00e4gt mehr als 1000m +sinfo_calc_flow_depth.warning.year_difference = {0}: Zeitliche Abweichung betr\u00e4gt {1} Jahre. Dies kann zu unplausiblen Ergebnissen f\u00fchren sinfo_calc_flow_depth_development=Flie\u00dftiefenentwicklung sinfo_calc_flow_depth_minmax=Minimale und Maximale Flie\u00dftiefe diff -r f762fadc5313 -r a536e1aacf0f artifacts/src/main/resources/messages_en.properties --- a/artifacts/src/main/resources/messages_en.properties Fri Feb 09 16:11:47 2018 +0100 +++ b/artifacts/src/main/resources/messages_en.properties Fri Feb 09 18:07:22 2018 +0100 @@ -773,6 +773,7 @@ sinfo_calc_flow_depth=Flie\u00dftiefen sinfo_calc_flow_depth.warning.missingQ = {0}: keine Abflussdaten vorhanden, Transportk\u00f6rperh\u00f6henberechnung nicht m\u00f6glich sinfo_calc_flow_depth.warning.waterlevel_discretisation = Wasserspiegel {0}: r\u00e4umliche Aufl\u00f6sung betr\u00e4gt mehr als 1000m +sinfo_calc_flow_depth.warning.year_difference = {0}: Zeitliche Abweichung betr\u00e4gt {1} Jahre. Dies kann zu unplausiblen Ergebnissen f\u00fchren sinfo_calc_flow_depth_development=Flie\u00dftiefenentwicklung sinfo_calc_flow_depth_minmax=Minimale und Maximale Flie\u00dftiefe