changeset 9394:439699ff9b2d

Added U-Info iota (prev. salix) calculation for historical scenario
author mschaefer
date Fri, 10 Aug 2018 17:31:46 +0200
parents 6174daaf5e56
children 0255c51283a4
files artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/BedHeightsFinder.java artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/DefaultBedHeights.java artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SalixLineAccess.java artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SalixLineCalculation.java artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SalixLineCalculator.java
diffstat 5 files changed, 111 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/BedHeightsFinder.java	Fri Aug 10 17:07:30 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/BedHeightsFinder.java	Fri Aug 10 17:31:46 2018 +0200
@@ -102,7 +102,7 @@
      *
      * @return <code>null</code> if no bed height with the given id exists.
      */
-    private static BedHeightsFinder forId(final Calculation problems, final int id, final DoubleRange range) {
+    public static BedHeightsFinder forId(final Calculation problems, final int id, final DoubleRange range) {
 
         final BedHeight bedHeight = BedHeight.getBedHeightById(id);
         if (bedHeight == null)
@@ -153,6 +153,12 @@
         return this.values.keySet();
     }
 
+    public DoubleRange getKmRange() {
+        if (this.values.isEmpty())
+            return null;
+        return new DoubleRange(this.values.firstKey().doubleValue(), this.values.lastKey().doubleValue());
+    }
+
     public double getMeanBedHeight(final double km) {
         return interpolateBedHeights(km, BedHeightValueType.value);
     }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/DefaultBedHeights.java	Fri Aug 10 17:07:30 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/DefaultBedHeights.java	Fri Aug 10 17:31:46 2018 +0200
@@ -22,11 +22,11 @@
 import org.dive4elements.river.model.River;
 
 /**
- * This class knows how to find the default bed heights defined for tkh calculation
+ * This class knows how to find the default bed heights defined for tkh and other calculations
  *
  * @author Gernot Belger
  */
-final class DefaultBedHeights {
+public final class DefaultBedHeights {
 
     private final River river;
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SalixLineAccess.java	Fri Aug 10 17:07:30 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SalixLineAccess.java	Fri Aug 10 17:31:46 2018 +0200
@@ -99,6 +99,18 @@
         return super.getString("supraregional_table");
     }
 
+    /**
+     * Database id of the selected sounding, or 0
+     */
+    public int getBedHeightId() {
+        final BedHeightAccess access = new BedHeightAccess(this.artifact);
+        final int[] ids = access.getBedHeightIDs();
+        if ((ids != null) && (ids.length > 0)) {
+            return ids[0];
+        }
+        return 0;
+    }
+
     public BedHeight getBedHeight() { // TODO: make lazy? Aber achtung, falls der user zurückgeht und ne andere Peilung auswählt...
         final BedHeightAccess access = new BedHeightAccess(this.artifact);
         final int[] ids = access.getBedHeightIDs();
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SalixLineCalculation.java	Fri Aug 10 17:07:30 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SalixLineCalculation.java	Fri Aug 10 17:31:46 2018 +0200
@@ -10,6 +10,7 @@
 package org.dive4elements.river.artifacts.uinfo.salix;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.NavigableMap;
 import java.util.TreeMap;
@@ -22,26 +23,39 @@
 import org.dive4elements.river.artifacts.model.CalculationResult;
 import org.dive4elements.river.artifacts.resources.Resources;
 import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider;
+import org.dive4elements.river.artifacts.sinfo.tkhstate.BedHeightsFinder;
+import org.dive4elements.river.artifacts.sinfo.tkhstate.BedHeightsUtils;
+import org.dive4elements.river.artifacts.sinfo.tkhstate.DefaultBedHeights;
 import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils;
 import org.dive4elements.river.artifacts.sinfo.util.RiverInfo;
 import org.dive4elements.river.artifacts.uinfo.UINFOArtifact;
 import org.dive4elements.river.artifacts.uinfo.salix.SalixLineAccess.ScenarioType;
+import org.dive4elements.river.model.BedHeight;
 import org.dive4elements.river.model.River;
+import org.dive4elements.river.utils.Formatter;
 
 /**
+ * Calculation of a iota (former salix) longitudinal section, optionally with a delta scenario
+ *
  * @author Domenico Nardi Tironi
+ * @author Matthias Schäfer
  *
  */
 final class SalixLineCalculation {
 
     private final CallContext context;
 
+    private Calculation problems;
+
     public SalixLineCalculation(final CallContext context) {
         this.context = context;
     }
 
+    /**
+     * Calculates the iota longitudinal section and delta scenario of a uinfo artifact
+     */
     public CalculationResult calculate(final UINFOArtifact uinfo) {
-        final Calculation problems = new Calculation();
+        this.problems = new Calculation();
 
         final String calcModeLabel = Resources.getMsg(this.context.getMeta(), uinfo.getCalculationMode().name());
         final String user = CalculationUtils.findArtifactUser(this.context, uinfo);
@@ -62,14 +76,14 @@
         final SalixLineCalculator calculator = new SalixLineCalculator(riverInfoProvider);
         final NavigableMap<Double, List<Double>> rangeScenarios = buildRangeScenarios(accessSalix);
 
-        calculator.execute(problems, uinfo, rangeScenarios, scenarioType, buildScenarioLabels(accessSalix), buildPartialRangeString(accessSalix),
+        calculator.execute(this.problems, uinfo, rangeScenarios, scenarioType, buildScenarioLabels(accessSalix), buildPartialRangeString(accessSalix),
                 buildAdditionalString(accessSalix), results);
 
-        return new CalculationResult(results, problems);
+        return new CalculationResult(results, this.problems);
     }
 
     /**
-     * Build a map of delta-Ws by from-km for the selected scenario
+     * Builds a map of delta-Ws by from-km for the selected scenario
      */
     private NavigableMap<Double, List<Double>> buildRangeScenarios(final SalixLineAccess access) {
         final NavigableMap<Double, List<Double>> rangeScenarios = new TreeMap<>();
@@ -78,8 +92,8 @@
                     access.getRegionalScenarioIntegers());
         else if (access.getScenario() == ScenarioType.SUPRAREGIONAL)
             fillRangeScenarios(rangeScenarios, access.getSupraRegionalString());
-        // TODO else if (access.getScenario().equals(ScenarioType.HISTORICAL.getKey()))
-        // historisches Szenario aus MSH etc.
+        else if (access.getScenario() == ScenarioType.HISTORICAL)
+            fillRangeScenarios(rangeScenarios, access, access.getFromPart().doubleValue(), access.getToPart().doubleValue(), access.getBedHeightId());
         else
             fillRangeScenarios(rangeScenarios, access);
 
@@ -87,23 +101,23 @@
     }
 
     /**
-     * Fill a map of delta-Ws with only one 0-delta for the whole calc range (no scenario)
+     * Fills a map of delta-Ws with only one 0-delta for the whole calc range (no scenario)
      */
     private void fillRangeScenarios(final NavigableMap<Double, List<Double>> rangeScenarios, final RangeAccess calcRange) {
         final List<Double> nulls = new ArrayList<>();
-        nulls.add(0.0);
+        nulls.add(null);
         rangeScenarios.put(Double.valueOf(calcRange.getLowerKm() - 0.0001), nulls);
     }
 
     /**
-     * Fill a map of delta-Ws by km-range from the regional scenario input data
+     * Fills a map of delta-Ws by km-range from the regional scenario input data
      */
-    private void fillRangeScenarios(final NavigableMap<Double, List<Double>> rangeScenarios, final RangeAccess calcRange, final double partFrom,
-            final double partTo, final int[] deltaWs) {
+    private void fillRangeScenarios(final NavigableMap<Double, List<Double>> rangeScenarios, final RangeAccess calcRange,
+            final double partFrom, final double partTo, final int[] deltaWs) {
         final List<Double> nulls = new ArrayList<>();
         final List<Double> dwsm = new ArrayList<>();
         for (int i = 0; i <= deltaWs.length - 1; i++) {
-            nulls.add(0.0);
+            nulls.add(null);
             dwsm.add(deltaWs[i] / 100.0);
         }
         rangeScenarios.put(Double.valueOf(calcRange.getLowerKm() - 0.0001), nulls);
@@ -112,20 +126,65 @@
     }
 
     /**
-     * Fill a map of delta-Ws by km-range from the supraregional scenario input data
+     * Fills a map of delta-Ws by km-range from the supraregional scenario input data
      * (the zones input by the user cover the calc range completely)
      */
     private void fillRangeScenarios(final NavigableMap<Double, List<Double>> rangeScenarios, final String zones) {
         final List<SalixZone> parts = SalixZone.parse(zones);
         for (final SalixZone part : parts) {
             final List<Double> dwsm = new ArrayList<>();
-            dwsm.add(part.getDwsplValue() / 100.0);
+            if (part.getDwsplValue() == 0)
+                dwsm.add(null);
+            else
+                dwsm.add(part.getDwsplValue() / 100.0);
             rangeScenarios.put(Double.valueOf(part.getFromKm().doubleValue() - 0.0001), dwsm);
         }
     }
 
     /**
-     * Build the list of delta-w labels for the scenario type
+     * Fetches historical and reference bed heights and fills a map of delta-MSHs for all fetched stations in the calc range
+     */
+    private void fillRangeScenarios(final NavigableMap<Double, List<Double>> rangeScenarios, final RangeAccess calcRange,
+            final double partFrom, final double partTo, final int historicalBedHeightId) {
+
+        // Find relevant default bed-heights
+        final River river = calcRange.getRiver();
+        final List<BedHeight> defaultBedHeights = new DefaultBedHeights(river).getBedHeights(this.problems);
+        if (defaultBedHeights.isEmpty())
+            return;
+        final DoubleRange scenarioRange = new DoubleRange(partFrom, partTo);
+        final Collection<BedHeightsFinder> allFinders = BedHeightsFinder.createTkhBedHeights(this.problems, scenarioRange, defaultBedHeights);
+        final Collection<BedHeightsFinder> currentFinders = new ArrayList<>(allFinders);
+
+        // Add historical bed-heights
+        final BedHeightsFinder historicalFinder = BedHeightsFinder.forId(this.problems, historicalBedHeightId, scenarioRange);
+        allFinders.add(historicalFinder);
+        final Collection<Double> stations = BedHeightsUtils.extractStationCollection(allFinders, true);
+        final List<Double> nulls = new ArrayList<>();
+        nulls.add(null);
+        rangeScenarios.put(Double.valueOf(calcRange.getLowerKm() - 0.0001), nulls);
+        for (final Double station : stations) {
+            rangeScenarios.put(station, new ArrayList<Double>());
+            rangeScenarios.get(station).add(Double.valueOf(bedHeightDifference(station.doubleValue(), currentFinders, historicalFinder)));
+        }
+        rangeScenarios.put(Double.valueOf(partTo + 0.0001), nulls);
+    }
+
+    /**
+     * Gets the difference of a historical bed height against a current one for a station
+     */
+    private double bedHeightDifference(final double station, final Collection<BedHeightsFinder> currentFinders, final BedHeightsFinder historicalFinder) {
+        double currentMSH = Double.NaN;
+        for (final BedHeightsFinder bhf : currentFinders) {
+            currentMSH = bhf.getMeanBedHeight(station);
+            if (!Double.isNaN(currentMSH))
+                break;
+        }
+        return Formatter.roundFlowDepth(historicalFinder.getMeanBedHeight(station)).subtract(Formatter.roundFlowDepth(currentMSH)).doubleValue();
+    }
+
+    /**
+     * Builds the list of delta-w labels for the scenario type
      */
     private String[] buildScenarioLabels(final SalixLineAccess access) {
         final List<String> labels = new ArrayList<>();
@@ -142,7 +201,7 @@
     }
 
     /**
-     * Build the km range string for the scenario type
+     * Builds the km range string for the scenario type
      */
     private String buildPartialRangeString(final SalixLineAccess access) {
         if ((access.getScenario() == ScenarioType.REGIONAL) || (access.getScenario() == ScenarioType.HISTORICAL)) {
@@ -166,7 +225,7 @@
     }
 
     /**
-     * Build the delta w or time string for the scenario type
+     * Builds the delta w or time string for the scenario type
      */
     private String buildAdditionalString(final SalixLineAccess access) {
         if (access.getScenario() == ScenarioType.REGIONAL) {
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SalixLineCalculator.java	Fri Aug 10 17:07:30 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SalixLineCalculator.java	Fri Aug 10 17:31:46 2018 +0200
@@ -139,7 +139,7 @@
 
         final ResultRow row = ResultRow.create();
         row.putValue(GeneralResultType.station, station);
-        // Find station's gauge
+        // Find station's gauge (obsolete version which calculates gauge-wise)
         // final Gauge gauge = this.riverInfoProvider.getGauge(station, true);
         // Interpolate mnw, mw, and mhw
         // final double mnw = interpolateW(station, this.gaugeMnwPos.get(gauge));
@@ -156,11 +156,11 @@
         row.putValue(UInfoResultType.salix_mw_mnw, calcMwmnw(mw, mnw));
         // Calc scenario values (always all scenario types set, Result variant extracts the fields needed)
         final List<SalixScenario> scenarios = new ArrayList<>();
-        final double[] deltaws = getDeltaWs(station, rangeScenarios);
-        for (int i = 0; i <= deltaws.length - 1; i++) {
-            if (Math.abs(deltaws[i]) > 0.0001) {
-                final double salix = calcSalix(mhw, mw + deltaws[i]);
-                scenarios.add(new SalixScenario((int) (deltaws[i] * 100), salix));
+        final List<Double> deltaws = getDeltaWs(station, rangeScenarios);
+        for (final Double deltaw : deltaws) {
+            if (deltaw != null) {
+                final double salix = calcSalix(mhw, mw + deltaw);
+                scenarios.add(new SalixScenario((int) (deltaw * 100), salix));
             }
             else {
                 scenarios.add(null);
@@ -198,16 +198,15 @@
     }
 
     /**
-     * Gets the station-specific list of delta-ws of the active scenario, at least with one 0 item in any case
+     * Gets the station-specific list of delta-ws of the active scenario, at least with one null item in any case
      */
-    private double[] getDeltaWs(final double station, final NavigableMap<Double, List<Double>> rangeScenarios) {
+    private List<Double> getDeltaWs(final double station, final NavigableMap<Double, List<Double>> rangeScenarios) {
         final Entry<Double, List<Double>> stationScenarios = rangeScenarios.floorEntry(station);
-        if (stationScenarios == null)
-            return new double[] { 0.0 };
-
-        final double[] deltaws = new double[stationScenarios.getValue().size()];
-        for (int i = 0; i <= stationScenarios.getValue().size() - 1; i++)
-            deltaws[i] = stationScenarios.getValue().get(i);
-        return deltaws;
+        if (stationScenarios != null) {
+            return stationScenarios.getValue();
+        }
+        final List<Double> noScen = new ArrayList<>();
+        noScen.add(null);
+        return noScen;
     }
 }
\ No newline at end of file

http://dive4elements.wald.intevation.org