diff artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/GaugeMainValueFinder.java @ 9202:b4402594213b

More work on calculations and output for S-Info flood duration workflow (chart types 1 and 2)
author mschaefer
date Mon, 02 Jul 2018 07:33:53 +0200
parents
children b38be7ea53e2
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/GaugeMainValueFinder.java	Mon Jul 02 07:33:53 2018 +0200
@@ -0,0 +1,239 @@
+/** 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.common;
+
+import java.util.Map.Entry;
+import java.util.NavigableMap;
+import java.util.TreeMap;
+
+import org.dive4elements.river.artifacts.model.Calculation;
+import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.MainValue;
+import org.dive4elements.river.model.MainValueType.MainValueTypeKey;
+
+/**
+ * Loading the main values of a gauge to find relative positions of a value and build a corresponding zone name
+ *
+ * @author Matthias Schäfer
+ *
+ */
+public final class GaugeMainValueFinder {
+
+    /***** FIELDS *****/
+
+    // private static Logger log = Logger.getLogger(GaugeMainValueNameFinder.class);
+
+    private final Gauge gauge;
+
+    private Calculation problems;
+
+    private final NavigableMap<Double, MainValue> mainValues;
+
+    private final MainValueTypeKey keyType;
+
+    private final String approxPrefix = "ca."; // "\u2248" geht wohl nicht
+
+    private Entry<Double, MainValue> foundCeiling;
+
+    private Entry<Double, MainValue> foundFloor;
+
+    private double foundRelativeDistance;
+
+
+    /***** CONSTRUCTORS *****/
+
+    private GaugeMainValueFinder(final MainValueTypeKey keyType, final Gauge gauge, final Calculation problems) {
+        this.gauge = gauge;
+        this.problems = problems;
+        this.keyType = keyType;
+        this.mainValues = new TreeMap<>();
+        for (final MainValue mainValue : MainValue.getValuesOfGaugeAndType(gauge, keyType))
+            this.mainValues.put(Double.valueOf(mainValue.getValue().doubleValue()), mainValue);
+        if (this.mainValues.isEmpty() && (this.problems != null)) {
+            this.problems.addProblem("gauge_main_values.missing", gauge.getName());
+            // Report only once
+            this.problems = null;
+        }
+    }
+
+
+    /***** METHODS *****/
+
+    /**
+     * Loads the the main values table of a type and a gauge (GAUGE.sta)
+     *
+     * @return The main values finder of a type and a gauge, or null
+     */
+    public static GaugeMainValueFinder loadValues(final MainValueTypeKey type, final Gauge gauge, final Calculation problems) {
+        return new GaugeMainValueFinder(type, gauge, problems);
+    }
+
+    /**
+     * If this provider may return valid data at all.
+     */
+    public boolean isValid() {
+        return (this.mainValues != null);
+    }
+
+    /**
+     * Searches the main value zone for a value, and returns a textual description of the zone
+     * (name for an exact match, circa expression for +/- 10% match, less-than/between/greater-than expression otherwise)
+     */
+    public String findZoneName(final double value) {
+        if (!this.findValue(value))
+            return "";
+
+        // Clearly below or just (max. 10%) below lowest named value
+        if (this.foundFloor == null) {
+            if (Double.isInfinite(this.foundRelativeDistance))
+                return "<" + this.foundCeiling.getValue().getMainValue().getName();
+            else
+                return this.approxPrefix + this.foundCeiling.getValue().getMainValue().getName();
+        }
+
+        // Clearly above or just (max. 10%) above highest named value
+        if (this.foundCeiling == null) {
+            if (Double.isInfinite(this.foundRelativeDistance))
+                return ">" + this.foundFloor.getValue().getMainValue().getName();
+            else
+                return this.approxPrefix + this.foundFloor.getValue().getMainValue().getName();
+        }
+
+        // Exact match
+        if (this.mainValues.containsKey(Double.valueOf(value)))
+            return this.mainValues.get(Double.valueOf(value)).getMainValue().getName();
+
+        // Near (10%) one of the borders of a zone interval, or clearly within a zone
+        if (this.foundRelativeDistance <= 0.001)
+            return this.foundFloor.getValue().getMainValue().getName();
+        else if (this.foundRelativeDistance <= 0.1)
+            return this.approxPrefix + this.foundFloor.getValue().getMainValue().getName();
+        else if (this.foundRelativeDistance >= 0.9)
+            return this.approxPrefix + this.foundCeiling.getValue().getMainValue().getName();
+        else
+            return this.foundFloor.getValue().getMainValue().getName() + "-" + this.foundCeiling.getValue().getMainValue().getName();
+    }
+
+    /**
+     * Searches the main value zone for a value, and returns the zone name for an exact match, the nomatchReturn otherwise
+     */
+    public String findExactZoneName(final double value, final String noMatchReturn) {
+        this.findValue(value);
+        if ((this.foundFloor != null) && (this.foundFloor.getKey() == this.foundCeiling.getKey()))
+            return this.foundFloor.getValue().getMainValue().getName();
+        else
+            return noMatchReturn;
+    }
+
+    /**
+     * Searches the interval of a main value and its relative distance from the lower value
+     */
+    public boolean findValue(final double value) {
+        this.foundFloor = null;
+        this.foundCeiling = null;
+        this.foundRelativeDistance = Double.NaN;
+        if (!this.isValid())
+            return false;
+        if (Double.isNaN(value))
+            return false;
+
+        // Clearly below or just (max. 10%) below lowest named value
+        this.foundFloor = this.mainValues.floorEntry(Double.valueOf(value));
+        if (this.foundFloor == null) {
+            this.foundCeiling = this.mainValues.firstEntry();
+            if (value >= this.mainValues.firstKey().doubleValue() * 0.9) {
+                this.foundRelativeDistance = 0.9;
+                return true;
+            }
+            else {
+                this.foundRelativeDistance = Double.NEGATIVE_INFINITY;
+                return false;
+            }
+        }
+
+        // Clearly above or just (max. 10%) above highest named value
+        this.foundCeiling = this.mainValues.ceilingEntry(Double.valueOf(value));
+        if (this.foundCeiling == null) {
+            if (value <= this.mainValues.lastKey().doubleValue() * 1.1) {
+                this.foundRelativeDistance = 0.1;
+                return true;
+            }
+            else {
+                this.foundRelativeDistance = Double.POSITIVE_INFINITY;
+                return false;
+            }
+        }
+
+        // Exact match or within an interval
+        if (this.foundCeiling.getKey() == this.foundFloor.getKey())
+            this.foundRelativeDistance = 0.0;
+        else
+            this.foundRelativeDistance = (value - this.foundFloor.getKey().doubleValue())
+            / (this.foundCeiling.getKey().doubleValue() - this.foundFloor.getKey().doubleValue());
+        return true;
+    }
+
+    /**
+     * Floor value of the last findValue
+     */
+    public MainValue getFoundFloorValue() {
+        if (this.foundFloor != null)
+            return this.foundFloor.getValue();
+        else
+            return null;
+    }
+
+    /**
+     * Ceiling value of the last findValue
+     */
+    public MainValue getFoundCeilingValue() {
+        if (this.foundCeiling != null)
+            return this.foundCeiling.getValue();
+        else
+            return null;
+    }
+
+    /**
+     * Relative distance of the last findValue
+     */
+    public double getFoundRelativeDistance() {
+        return this.getFoundRelativeDistance();
+    }
+
+    /**
+     * Searches a pair of zone names and return the a value within the interval by a relative distance, or NaN
+     */
+    public double findValue(final String floorZone, final String ceilingZone, final double relativeDistance) {
+        this.foundFloor = null;
+        this.foundCeiling = null;
+        this.foundRelativeDistance = relativeDistance;
+        for (final Entry<Double, MainValue> mainValue : this.mainValues.entrySet()) {
+            if (mainValue.getValue().getMainValue().getName().equalsIgnoreCase(floorZone)) {
+                this.foundFloor = mainValue;
+                break;
+            }
+        }
+        if (this.foundFloor == null)
+            return Double.NaN;
+        if (floorZone.equalsIgnoreCase(ceilingZone))
+            return this.foundFloor.getKey().doubleValue();
+        for (final Entry<Double, MainValue> mainValue : this.mainValues.entrySet()) {
+            if (mainValue.getValue().getMainValue().getName().equalsIgnoreCase(ceilingZone)) {
+                this.foundCeiling = mainValue;
+                break;
+            }
+        }
+        if (this.foundCeiling == null)
+            return Double.NaN;
+        else
+            return (this.foundCeiling.getKey().doubleValue() - this.foundFloor.getKey().doubleValue()) * this.foundRelativeDistance
+                    + this.foundFloor.getKey().doubleValue();
+    }
+}
\ No newline at end of file

http://dive4elements.wald.intevation.org