sascha@146: package de.intevation.flys.artifacts.model;
sascha@146: 
sascha@146: import java.util.List;
sascha@146: import java.util.Map;
sascha@146: import java.util.HashMap;
sascha@146: import java.util.Arrays;
sascha@146: 
sascha@146: import java.io.Serializable;
sascha@146: 
sascha@146: import org.hibernate.Session;
sascha@146: import org.hibernate.Query;
sascha@146: 
sascha@146: import org.apache.log4j.Logger;
sascha@146: 
ingo@315: import de.intevation.flys.backend.SessionHolder;
sascha@146: import de.intevation.flys.model.Gauge;
sascha@146: import de.intevation.flys.model.DischargeTable;
sascha@146: import de.intevation.flys.model.DischargeTableValue;
sascha@146: 
sascha@2418: import gnu.trove.TDoubleArrayList;
sascha@2418: 
felix@3050: /** Documentation goes here. */
sascha@146: public class DischargeTables
sascha@146: implements   Serializable
sascha@146: {
felix@3050:     /** Private logger. */
sascha@329:     private static Logger log = Logger.getLogger(DischargeTables.class);
sascha@329: 
ingo@4138:     /** Scale to convert discharge table values of master table into [cm] */
ingo@4138:     public static final double MASTER_SCALE = 100d;
ingo@4138: 
ingo@4138:     /** Scale to convert discharge table values of historical tables into [cm] */
ingo@4138:     public static final double HISTORICAL_SCALE = 1d;
sascha@328: 
sascha@328:     public static final int MASTER = 0;
sascha@328: 
sascha@146:     protected List<String> gaugeNames;
sascha@146: 
sascha@146:     protected String riverName;
sascha@146: 
sascha@154:     protected double scale;
sascha@154: 
sascha@328:     protected int    kind;
sascha@328: 
sascha@150:     protected Map<String, double [][]> values;
sascha@146: 
sascha@146:     public DischargeTables() {
sascha@146:     }
sascha@146: 
sascha@328:     public DischargeTables(String riverName, String gaugeName) {
sascha@328:         this(riverName, gaugeName, MASTER);
sascha@146:     }
sascha@146: 
sascha@328:     public DischargeTables(String riverName, String gaugeName, int kind) {
sascha@328:         this(riverName, new String [] { gaugeName }, kind);
sascha@328:     }
sascha@328: 
sascha@328:     public DischargeTables(String riverName, String [] gaugeNames) {
sascha@328:         this(riverName, gaugeNames, MASTER);
sascha@328:     }
sascha@328: 
sascha@328:     public DischargeTables(String riverName, String [] gaugeNames, int kind) {
sascha@328:         this(riverName, Arrays.asList(gaugeNames), kind);
sascha@328:     }
sascha@328: 
sascha@328:     public DischargeTables(
sascha@328:         String       riverName,
sascha@328:         List<String> gaugeNames,
sascha@328:         int          kind
sascha@328:     ) {
sascha@154:         scale           = Double.NaN;
sascha@328:         this.kind       = kind;
sascha@146:         this.riverName  = riverName;
sascha@146:         this.gaugeNames = gaugeNames;
sascha@146:     }
sascha@146: 
sascha@328:     public double [][] getFirstTable() {
ingo@4138:         return getFirstTable(MASTER_SCALE);
sascha@328:     }
sascha@328: 
sascha@328:     public double [][] getFirstTable(double scale) {
sascha@328:         Map<String, double [][]> values = getValues(scale);
sascha@328:         for (double [][] table: values.values()) {
sascha@328:             return table;
sascha@328:         }
sascha@328:         return null;
sascha@328:     }
sascha@328: 
sascha@328:     public Map<String, double [][]> getValues() {
ingo@4138:         return getValues(MASTER_SCALE);
sascha@328:     }
sascha@328: 
ingo@153:     public Map<String, double [][]> getValues(double scale) {
sascha@154:         if (values == null || scale != this.scale) {
ingo@153:             values = loadValues(scale);
sascha@154:             this.scale = scale;
sascha@146:         }
sascha@146:         return values;
sascha@146:     }
sascha@146: 
felix@1929:     /**
felix@1929:      * Returns mapping of gauge name to values.
felix@1929:      */
ingo@153:     protected Map<String, double [][]> loadValues(double scale) {
sascha@150:         Map<String, double [][]> values = new HashMap<String, double [][]>();
sascha@146: 
sascha@311:         Session session = SessionHolder.HOLDER.get();
sascha@146: 
sascha@311:         Query gaugeQuery = session.createQuery(
ingo@336:             "from Gauge where name=:gauge and river.name=:river");
sascha@311:         gaugeQuery.setParameter("river", riverName);
sascha@146: 
sascha@311:         for (String gaugeName: gaugeNames) {
sascha@311:             gaugeQuery.setParameter("gauge", gaugeName);
sascha@311:             List<Gauge> gauges = gaugeQuery.list();
sascha@311:             if (gauges.isEmpty()) {
sascha@311:                 log.warn(
sascha@311:                     "no gauge '"+gaugeName+"' at river '"+riverName+"'");
sascha@311:                 continue;
sascha@311:             }
sascha@311:             Gauge gauge = gauges.get(0);
sascha@146: 
sascha@311:             List<DischargeTable> tables = gauge.getDischargeTables();
sascha@146: 
sascha@311:             if (tables.isEmpty()) {
sascha@311:                 log.warn(
sascha@311:                     "no discharge table for gauge '" + gaugeName + "'");
sascha@311:                 continue;
sascha@146:             }
sascha@146: 
sascha@311:             // TODO: Filter by time interval
sascha@311:             DischargeTable table = tables.get(0);
sascha@311: 
sascha@2418:             double [][] vs = loadDischargeTableValues(table, scale);
sascha@329: 
sascha@2418:             values.put(gaugeName, vs);
sascha@146:         }
sascha@311: 
sascha@311:         return values;
sascha@146:     }
sascha@329: 
ingo@2230: 
ingo@2230:     /**
ingo@2230:      * @param table The discharge table
ingo@2230:      * @param scale The scale factor to adjust W and Q values.
ingo@2230:      *
ingo@2230:      * @return the values of a discharge table.
ingo@2230:      */
ingo@2230:     public static double[][] loadDischargeTableValues(
ingo@2230:         DischargeTable table,
sascha@2418:         double         scale
ingo@2230:     ) {
ingo@2230:         List<DischargeTableValue> dtvs = table.getDischargeTableValues();
ingo@2230: 
ingo@2230:         final double [][] vs = new double[2][dtvs.size()];
ingo@2230: 
ingo@2230:         int idx = 0;
ingo@2230:         for (DischargeTableValue dtv: dtvs) {
ingo@2230:             double q = dtv.getQ().doubleValue();
ingo@2230:             vs[0][idx] = q * scale;
ingo@2230:             vs[1][idx] = dtv.getW().doubleValue() * scale;
ingo@2230:             ++idx;
ingo@2230:         }
ingo@2230: 
ingo@2230:         return vs;
ingo@2230:     }
ingo@2230: 
sascha@2418:     private static final double EPSILON = 1e-5;
ingo@2230: 
sascha@2418:     private static final boolean epsEquals(double a, double b) {
sascha@2418:         return Math.abs(a - b) < EPSILON;
sascha@2418:     }
sascha@2418: 
sascha@2418:     private static final boolean between(double a, double b, double x) {
sascha@2418:         if (a > b) { double t = a; a = b; b = t; }
sascha@2418:         return x > a && x < b;
sascha@2418:     }
sascha@2418: 
sascha@2418:     public static double [] getQsForW(double [][] values, double w) {
sascha@378: 
sascha@378:         boolean debug = log.isDebugEnabled();
sascha@378: 
sascha@378:         if (debug) {
sascha@2418:             log.debug("getQsForW: W = " + w);
sascha@329:         }
sascha@329: 
sascha@2418:         double [] qs = values[0];
sascha@2418:         double [] ws = values[1];
sascha@329: 
sascha@2418:         int N = Math.min(qs.length, ws.length);
sascha@329: 
sascha@2418:         if (N == 0) {
sascha@378:             if (debug) {
sascha@2418:                 log.debug("Q(" + w + ") = []");
sascha@2418:             }
sascha@2418:             return new double [0];
sascha@2418:         }
sascha@2418: 
sascha@2418:         TDoubleArrayList outQs = new TDoubleArrayList();
sascha@2418: 
sascha@2418:         if (epsEquals(ws[0], w)) {
sascha@2418:             outQs.add(qs[0]);
sascha@2418:         }
sascha@2418: 
sascha@2418:         for (int i = 1; i < N; ++i) {
sascha@2418:             if (epsEquals(ws[i], w)) {
sascha@2418:                 outQs.add(qs[i]);
sascha@2418:             }
sascha@2418:             else if (between(ws[i-1], ws[i], w)) {
sascha@2418:                 double w1 = ws[i-1];
sascha@2418:                 double w2 = ws[i];
sascha@2418:                 double q1 = qs[i-1];
sascha@2418:                 double q2 = qs[i];
sascha@2418: 
sascha@2418:                 // q1 = m*w1 + b
sascha@2418:                 // q2 = m*w2 + b
sascha@2418:                 // q2 - q1 = m*(w2 - w1)
sascha@2418:                 // m = (q2 - q1)/(w2 - w1) # w2 != w1
sascha@2418:                 // b = q1 - m*w1
sascha@2418:                 // w1 != w2
sascha@2418: 
sascha@2418:                 double m = (q2 - q1)/(w2 - w1);
sascha@2418:                 double b = q1 - m*w1;
sascha@2418:                 double q = w*m + b;
sascha@2418: 
sascha@2418:                 outQs.add(q);
sascha@378:             }
sascha@329:         }
sascha@2418: 
sascha@2418:         double [] result = outQs.toNativeArray();
sascha@2418: 
sascha@2418:         if (debug) {
sascha@2418:             log.debug("Q(" + w + ") = " + Arrays.toString(result));
sascha@378:         }
sascha@2418: 
sascha@2418:         return result;
sascha@329:     }
teichmann@4226: 
teichmann@4226:     public static double [] getWsForQ(double [][] values, double q) {
teichmann@4226: 
teichmann@4226:         boolean debug = log.isDebugEnabled();
teichmann@4226: 
teichmann@4226:         if (debug) {
teichmann@4226:             log.debug("getWsForQ: W = " + q);
teichmann@4226:         }
teichmann@4226: 
teichmann@4226:         double [] qs = values[0];
teichmann@4226:         double [] ws = values[1];
teichmann@4226: 
teichmann@4226:         int N = Math.min(qs.length, ws.length);
teichmann@4226: 
teichmann@4226:         if (N == 0) {
teichmann@4226:             if (debug) {
teichmann@4226:                 log.debug("W(" + q + ") = []");
teichmann@4226:             }
teichmann@4226:             return new double [0];
teichmann@4226:         }
teichmann@4226: 
teichmann@4226:         TDoubleArrayList outWs = new TDoubleArrayList();
teichmann@4226: 
teichmann@4226:         if (epsEquals(qs[0], q)) {
teichmann@4226:             outWs.add(ws[0]);
teichmann@4226:         }
teichmann@4226: 
teichmann@4226:         for (int i = 1; i < N; ++i) {
teichmann@4226:             if (epsEquals(qs[i], q)) {
teichmann@4226:                 outWs.add(ws[i]);
teichmann@4226:             }
teichmann@4226:             else if (between(qs[i-1], qs[i], q)) {
teichmann@4226:                 double w1 = ws[i-1];
teichmann@4226:                 double w2 = ws[i];
teichmann@4226:                 double q1 = qs[i-1];
teichmann@4226:                 double q2 = qs[i];
teichmann@4226: 
teichmann@4226:                 // w1 = m*q1 + b
teichmann@4226:                 // w2 = m*q2 + b
teichmann@4226:                 // w2 - w1 = m*(q2 - q1)
teichmann@4226:                 // m = (w2 - w1)/(q2 - q1) # q2 != q1
teichmann@4226:                 // b = w1 - m*q1
teichmann@4226:                 // q1 != q2
teichmann@4226: 
teichmann@4226:                 double m = (w2 - w1)/(q2 - q1);
teichmann@4226:                 double b = w1 - m*q1;
teichmann@4226:                 double w = q*m + b;
teichmann@4226: 
teichmann@4226:                 outWs.add(w);
teichmann@4226:             }
teichmann@4226:         }
teichmann@4226: 
teichmann@4226:         double [] result = outWs.toNativeArray();
teichmann@4226: 
teichmann@4226:         if (debug) {
teichmann@4226:             log.debug("W(" + q + ") = " + Arrays.toString(result));
teichmann@4226:         }
teichmann@4226: 
teichmann@4226:         return result;
teichmann@4226:     }
sascha@146: }
sascha@146: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :