mschaefer@9176: /** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde mschaefer@9176: * Software engineering by mschaefer@9176: * Björnsen Beratende Ingenieure GmbH mschaefer@9176: * Dr. Schumacher Ingenieurbüro für Wasser und Umwelt mschaefer@9176: * mschaefer@9176: * This file is Free Software under the GNU AGPL (>=v3) mschaefer@9176: * and comes with ABSOLUTELY NO WARRANTY! Check out the mschaefer@9176: * documentation coming with Dive4Elements River for details. mschaefer@9176: */ mschaefer@9176: package org.dive4elements.river.artifacts.sinfo.common; mschaefer@9176: mschaefer@9176: import org.apache.commons.lang.math.DoubleRange; mschaefer@9176: import org.apache.commons.math.FunctionEvaluationException; mschaefer@9176: import org.apache.commons.math.analysis.UnivariateRealFunction; mschaefer@9176: import org.apache.commons.math.analysis.interpolation.LinearInterpolator; mschaefer@9176: import org.dive4elements.river.artifacts.model.Calculation; mschaefer@9176: import org.dive4elements.river.model.Gauge; mschaefer@9176: import org.dive4elements.river.model.MainValue; mschaefer@9176: import org.dive4elements.river.model.MainValueType.MainValueTypeKey; mschaefer@9176: mschaefer@9176: import gnu.trove.TDoubleArrayList; mschaefer@9176: mschaefer@9176: /** mschaefer@9176: * Loading and search/interpolate the duration main values of a gauge mschaefer@9176: * mschaefer@9176: * @author Matthias Schäfer mschaefer@9176: * mschaefer@9176: */ mschaefer@9176: public final class GaugeDurationValuesFinder { mschaefer@9176: mschaefer@9176: /***** FIELDS *****/ mschaefer@9176: mschaefer@9176: // private static Logger log = Logger.getLogger(GaugeDurationValuesFinder.class); mschaefer@9176: mschaefer@9176: private final Gauge gauge; mschaefer@9176: mschaefer@9176: private Calculation problems; mschaefer@9176: mschaefer@9176: private UnivariateRealFunction qInterpolator; mschaefer@9176: mschaefer@9176: private DoubleRange qRange; mschaefer@9176: mschaefer@9176: private UnivariateRealFunction durInterpolator; mschaefer@9176: mschaefer@9176: private DoubleRange durRange; mschaefer@9176: mschaefer@9176: private final String approxPrefix = "ca."; // "\u2248" geht wohl nicht mschaefer@9176: mschaefer@9176: mschaefer@9176: /***** CONSTRUCTORS *****/ mschaefer@9176: mschaefer@9176: private GaugeDurationValuesFinder(final Gauge gauge, final Calculation problems) { mschaefer@9176: this.gauge = gauge; mschaefer@9176: this.problems = problems; mschaefer@9176: final TDoubleArrayList qs = new TDoubleArrayList(); mschaefer@9176: final TDoubleArrayList durs = new TDoubleArrayList(); mschaefer@9176: for (final MainValue v : MainValue.getValuesOfGaugeAndType(gauge, MainValueTypeKey.DURATION)) { mschaefer@9176: qs.add(v.getValue().doubleValue()); mschaefer@9176: durs.add(Integer.valueOf(v.getMainValue().getName()).doubleValue()); mschaefer@9176: } mschaefer@9176: try { mschaefer@9176: this.qInterpolator = new LinearInterpolator().interpolate(qs.toNativeArray(), durs.toNativeArray()); mschaefer@9176: this.qRange = new DoubleRange(qs.get(0), qs.get(qs.size() - 1)); mschaefer@9176: } mschaefer@9176: catch (final Exception e) { mschaefer@9176: this.qInterpolator = null; mschaefer@9176: this.qRange = null; mschaefer@9176: } mschaefer@9176: qs.clear(); mschaefer@9176: durs.clear(); mschaefer@9176: for (final MainValue v : MainValue.getDurationDischargesOfGauge(gauge)) { mschaefer@9176: durs.add(Integer.valueOf(v.getMainValue().getName()).doubleValue()); mschaefer@9176: qs.add(v.getValue().doubleValue()); mschaefer@9176: } mschaefer@9176: try { mschaefer@9176: this.durInterpolator = new LinearInterpolator().interpolate(durs.toNativeArray(), qs.toNativeArray()); mschaefer@9176: this.durRange = new DoubleRange(durs.get(0), durs.get(durs.size() - 1)); mschaefer@9176: } mschaefer@9176: catch (final Exception e) { mschaefer@9176: this.durInterpolator = null; mschaefer@9176: this.durRange = null; mschaefer@9176: } mschaefer@9176: if (((this.qInterpolator == null) || (this.durInterpolator == null)) && (this.problems != null)) { mschaefer@9176: this.problems.addProblem("gauge_duration.missing", gauge.getName()); mschaefer@9176: // Report only once mschaefer@9176: this.problems = null; mschaefer@9176: } mschaefer@9176: } mschaefer@9176: mschaefer@9176: mschaefer@9176: /***** METHODS *****/ mschaefer@9176: mschaefer@9176: /** mschaefer@9176: * Loads the the discharge-duration table of a gauge (GAUGE.glt) mschaefer@9176: * mschaefer@9176: * @return The main values finder of a a gauge, or null mschaefer@9176: */ mschaefer@9176: public static GaugeDurationValuesFinder loadValues(final Gauge gauge, final Calculation problems) { mschaefer@9176: return new GaugeDurationValuesFinder(gauge, problems); mschaefer@9176: } mschaefer@9176: mschaefer@9176: /** mschaefer@9176: * If this provider may return valid data at all. mschaefer@9176: */ mschaefer@9176: public boolean isValid() { mschaefer@9176: return (this.qInterpolator != null); mschaefer@9176: } mschaefer@9176: mschaefer@9176: /** mschaefer@9176: * Discharge for a duration mschaefer@9176: * mschaefer@9176: * @return Q, or NegInf for duration less than all, or PosInf for duration greater then all, or NaN in case of exception mschaefer@9176: */ mschaefer@9176: public double getDischarge(final int duration) { mschaefer@9176: try { mschaefer@9176: if (this.durInterpolator == null) mschaefer@9176: return Double.NaN; mschaefer@9176: else if (duration < this.durRange.getMinimumDouble()) mschaefer@9176: return Double.NEGATIVE_INFINITY; mschaefer@9176: else if (duration > this.durRange.getMaximumDouble()) mschaefer@9176: return Double.POSITIVE_INFINITY; mschaefer@9176: else mschaefer@9176: return this.durInterpolator.value(duration); mschaefer@9176: } mschaefer@9176: catch (@SuppressWarnings("unused") final FunctionEvaluationException e) { mschaefer@9176: // ignore exception because this can/will happen regularly mschaefer@9176: return Double.NaN; mschaefer@9176: } mschaefer@9176: } mschaefer@9176: mschaefer@9176: /** mschaefer@9176: * Duration for a discharge mschaefer@9176: * mschaefer@9176: * @return duration, or 0 for Q less than all, or 365 for duration greater then all, or -1 in case of exception mschaefer@9176: */ mschaefer@9176: public double getDuration(final double q) { mschaefer@9176: try { mschaefer@9176: if (this.qInterpolator == null) mschaefer@9176: return -1; mschaefer@9176: else if (q < this.qRange.getMinimumDouble()) mschaefer@9176: return 0; mschaefer@9176: else if (q > this.qRange.getMaximumDouble()) mschaefer@9176: return 365; mschaefer@9176: else mschaefer@9176: return this.qInterpolator.value(q); mschaefer@9176: } mschaefer@9176: catch (@SuppressWarnings("unused") final FunctionEvaluationException e) { mschaefer@9176: // ignore exception because this can/will happen regularly mschaefer@9176: return -1; mschaefer@9176: } mschaefer@9176: } mschaefer@9176: }