diff artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/WQBaseTableFinder.java @ 9176:1614cb14308f

Work on calculations for S-Info flood duration workflow
author mschaefer
date Mon, 25 Jun 2018 19:21:11 +0200
parents
children
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/WQBaseTableFinder.java	Mon Jun 25 19:21:11 2018 +0200
@@ -0,0 +1,185 @@
+/** 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.ArrayList;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.NavigableMap;
+import java.util.TreeMap;
+
+import org.apache.commons.math.FunctionEvaluationException;
+import org.apache.commons.math.analysis.interpolation.LinearInterpolator;
+import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
+import org.dive4elements.river.artifacts.math.Linear;
+import org.dive4elements.river.artifacts.model.Calculation;
+import org.dive4elements.river.backend.SessionHolder;
+import org.dive4elements.river.model.River;
+import org.hibernate.SQLQuery;
+import org.hibernate.Session;
+import org.hibernate.type.StandardBasicTypes;
+
+import gnu.trove.TDoubleArrayList;
+
+/**
+ * Loading and search/interpolation of a W/Q base table of a river
+ *
+ * @author Matthias Schäfer
+ *
+ */
+public final class WQBaseTableFinder {
+
+    /***** FIELDS *****/
+
+    // private static Logger log = Logger.getLogger(WQTableFinder.class);
+
+    private final River river;
+
+    private Calculation problems;
+
+    private final List<String> columnNames;
+
+    private final NavigableMap<Double, PolynomialSplineFunction> kmWs;
+
+    private final NavigableMap<Double, PolynomialSplineFunction> kmQs;
+
+    private static final String SQLCOLUMNS = "SELECT wc.position AS colindex, wc.name AS qzone"
+            + " FROM wsts w"
+            + "  INNER JOIN wst_columns wc ON w.id=wc.wst_id"
+            + " WHERE w.river_id = :river_id"
+            + "  AND w.kind = 0"
+            + " ORDER BY wc.position ASC";
+
+    private static final String SQLMAIN = "SELECT wcv.position AS station, wc.position AS colindex, wcv.w, wqr.q"
+            + " FROM wsts w"
+            + "  INNER JOIN wst_columns wc ON w.id=wc.wst_id"
+            + "  INNER JOIN wst_column_values wcv ON wc.id=wcv.wst_column_id"
+            + "  INNER JOIN wst_column_q_ranges wcqr ON wc.id=wcqr.wst_column_id"
+            + "  INNER JOIN wst_q_ranges wqr ON wcqr.wst_q_range_id=wqr.id"
+            + "  INNER JOIN ranges r ON wqr.range_id=r.id AND wcv.position BETWEEN r.a AND r.b"
+            + " WHERE w.river_id = :river_id"
+            + "  AND w.kind = 0"
+            + "  AND r.river_id = :river_id"
+            + "  AND wcv.position BETWEEN :kmfrom - 1 AND :kmto + 1" // some tolerance for start and end of list
+            + " ORDER BY wcv.position ASC, "
+            + "  wc.position ASC";
+
+
+    /***** CONSTRUCTORS *****/
+
+    private WQBaseTableFinder(final River river, final Calculation problems, final List<Object[]> colnames, final List<Object[]> rows) {
+        this.river = river;
+        this.problems = problems;
+        this.columnNames = new ArrayList<>();
+        for (final Object[] colname : colnames)
+            this.columnNames.add(colname[1].toString());
+        this.kmWs = new TreeMap<>();
+        this.kmQs = new TreeMap<>();
+        final TDoubleArrayList ws = new TDoubleArrayList();
+        final TDoubleArrayList qs = new TDoubleArrayList();
+        double km = Double.NaN;
+        for (int i = 0; i <= rows.size() - 1; i++) {
+            if (ws.isEmpty() || ((double) rows.get(i)[0] <= km + 0.0001)) {
+                if (ws.isEmpty())
+                    km = (double) rows.get(i)[0];
+                ws.add((double) rows.get(i)[2]);
+                qs.add((double) rows.get(i)[3]);
+            }
+            if ((i == rows.size() - 1) || ((double) rows.get(i)[0] > km + 0.0001)) {
+                try {
+                    this.kmWs.put(km, new LinearInterpolator().interpolate(ws.toNativeArray(), qs.toNativeArray()));
+                    this.kmQs.put(km, new LinearInterpolator().interpolate(qs.toNativeArray(), ws.toNativeArray()));
+                }
+                catch (final Exception e) {
+                    if (this.problems != null) {
+                        this.problems.addProblem(km, "wq_base_data.missing");
+                        // Report only once
+                        this.problems = null;
+                    }
+                }
+                ws.clear();
+                qs.clear();
+                km = (double) rows.get(i)[0];
+                ws.add((double) rows.get(i)[2]);
+                qs.add((double) rows.get(i)[3]);
+            }
+        }
+    }
+
+
+    /***** METHODS *****/
+
+    /**
+     * Loads the the W/Q tables of a km range of a river
+     *
+     * @return The W/Q table finder of the river, or null
+     */
+    public static WQBaseTableFinder loadValues(final River river, final double fromKm, final double toKm, final Calculation problems) {
+        final Session session = SessionHolder.HOLDER.get();
+        final SQLQuery colQuery = session.createSQLQuery(SQLCOLUMNS)
+                .addScalar("colindex", StandardBasicTypes.INTEGER)
+                .addScalar("qzone", StandardBasicTypes.STRING);
+        colQuery.setParameter("river_id", river.getId());
+        final List<Object[]> colnames = colQuery.list();
+        if ((colnames == null) || colnames.isEmpty()) {
+            problems.addProblem("wq_table.missing");
+            return null;
+        }
+        final SQLQuery wqQuery = session.createSQLQuery(SQLMAIN)
+                .addScalar("station", StandardBasicTypes.DOUBLE)
+                .addScalar("colindex", StandardBasicTypes.INTEGER)
+                .addScalar("w", StandardBasicTypes.DOUBLE)
+                .addScalar("q", StandardBasicTypes.DOUBLE);
+        wqQuery.setParameter("river_id", river.getId());
+        wqQuery.setParameter("kmfrom", fromKm);
+        wqQuery.setParameter("kmto", toKm);
+        final List<Object[]> rows = wqQuery.list();
+        if ((rows != null) && !rows.isEmpty())
+            return new WQBaseTableFinder(river, problems, colnames, rows);
+        else {
+            problems.addProblem("wq_table.missing");
+            return null;
+        }
+    }
+
+    /**
+     * If this provider may return valid data at all.
+     */
+    public boolean isValid() {
+        return (this.kmWs != null);
+    }
+
+    /**
+     * Discharge for a W
+     *
+     * @param station
+     *            station to find or interpolate
+     * @param w
+     *            W in m+NN or m+NHN
+     * @return Q, or NegInf for w less than all, or PosInf for w greater then all, or NaN in case of exception
+     */
+    public double getDischarge(final double station, final double w) {
+        if (this.kmWs == null)
+            return Double.NaN;
+        final Entry<Double, PolynomialSplineFunction> lowerEntry = this.kmWs.floorEntry(Double.valueOf(station));
+        final Entry<Double, PolynomialSplineFunction> upperEntry = this.kmWs.ceilingEntry(Double.valueOf(station));
+        if ((lowerEntry == null) || (upperEntry == null))
+            return Double.NaN;
+        try {
+            final double lowerQ = lowerEntry.getValue().value(w);
+            final double upperQ = upperEntry.getValue().value(w);
+            return Linear.linear(station, lowerEntry.getKey().doubleValue(), upperEntry.getKey().doubleValue(), lowerQ, upperQ);
+        }
+        catch (@SuppressWarnings("unused") final FunctionEvaluationException e) {
+            // ignore exception because this can/will happen regularly
+            return Double.NaN;
+        }
+    }
+}
\ No newline at end of file

http://dive4elements.wald.intevation.org