changeset 8883:a536e1aacf0f

Further work on SINFO-FlowDepth
author gernotbelger
date Fri, 09 Feb 2018 18:07:22 +0100
parents f762fadc5313
children 7a8c12706834
files artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixRealizingCalculationExtended.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/BedHeightStationComparator.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/FlowDepthCalculation.java artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelData.java artifacts/src/main/java/org/dive4elements/river/artifacts/states/WaterlevelFetcher.java artifacts/src/main/resources/messages.properties artifacts/src/main/resources/messages_de.properties artifacts/src/main/resources/messages_de_DE.properties artifacts/src/main/resources/messages_en.properties
diffstat 9 files changed, 224 insertions(+), 53 deletions(-) [+]
line wrap: on
line diff
--- /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<Column> columns = getEventColumns(overview, cc);
+        for (final Column column : columns) {
+            final Date date = column.getDate();
+        }
+
+        return 1999;
+    }
+}
--- /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<BedHeightValue> {
+
+    @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
--- 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<BedHeightValue> values = bedHeight.getValues();
 
-            // REMARK: access the gauge once only during calculation
-            final Gauge gauge = findGauge(waterlevel, refGauge, gaugeIndex, km);
+        final List<BedHeightValue> 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
+}
--- 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 <code>true</code>, 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;
+    }
 }
--- 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<Segment> 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
--- 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
--- 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
--- 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
--- 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

http://dive4elements.wald.intevation.org