Mercurial > dive4elements > river
view artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/GaugeMainValueFinder.java @ 9268:ae9dee74e43e
Fixed: current station of duration chart was not changed
author | gernotbelger |
---|---|
date | Wed, 18 Jul 2018 18:53:15 +0200 |
parents | b38be7ea53e2 |
children | d9fda7af24ca |
line wrap: on
line source
/** 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 = "\u2248"; 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) { this.findValue(value); if (Double.isNaN(this.foundRelativeDistance)) 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(); } }