diff artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SalixLineCalculation.java @ 9394:439699ff9b2d

Added U-Info iota (prev. salix) calculation for historical scenario
author mschaefer
date Fri, 10 Aug 2018 17:31:46 +0200
parents 2da486c7c05f
children 6e7094368e97
line wrap: on
line diff
--- 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) {

http://dive4elements.wald.intevation.org