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@329: import java.util.Comparator;
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@146: public class DischargeTables
sascha@146: implements   Serializable
sascha@146: {
sascha@329:     private static Logger log = Logger.getLogger(DischargeTables.class);
sascha@329: 
sascha@328:     public static final double DEFAULT_SCALE = 100.0;
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() {
sascha@328:         return getFirstTable(DEFAULT_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() {
sascha@328:         return getValues(DEFAULT_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: 
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@311:             List<DischargeTableValue> dtvs =
sascha@311:                 table.getDischargeTableValues();
sascha@311: 
sascha@333:             final double [][] vs = new double[2][dtvs.size()];
sascha@333: 
sascha@333:             boolean qSorted = true;
sascha@333: 
sascha@333:             double lastQ = -Double.MAX_VALUE;
sascha@311: 
sascha@311:             int idx = 0;
sascha@311:             for (DischargeTableValue dtv: dtvs) {
sascha@333:                 double q = dtv.getQ().doubleValue();
sascha@333:                 vs[0][idx] = q * scale;
sascha@311:                 vs[1][idx] = dtv.getW().doubleValue() * scale;
sascha@311:                 ++idx;
sascha@333: 
sascha@333:                 if (qSorted && lastQ > q) {
sascha@333:                     qSorted = false;
sascha@333:                 }
sascha@333:                 lastQ = q;
sascha@311:             }
sascha@311: 
sascha@333:             if (!qSorted) {
sascha@333:                 log.debug("need to sort by q values");
sascha@333:                 // TODO: Do this db level.
sascha@333:                 // XXX: This is so ugly :-(
sascha@333:                 Integer [] indices = new Integer[vs[0].length];
sascha@333:                 for (int i = 0; i < indices.length; ++i) {
sascha@333:                     indices[i] = i;
sascha@333:                 }
sascha@329: 
sascha@333:                 Arrays.sort(indices, new Comparator<Integer>() {
sascha@333:                     public int compare(Integer a, Integer b) {
sascha@333:                         double va = vs[1][a];
sascha@333:                         double vb = vs[1][b];
sascha@333:                         double d = va - vb;
sascha@333:                         if (d < 0.0) return -1;
sascha@333:                         if (d > 0.0) return +1;
sascha@333:                         return 0;
sascha@333:                     }
sascha@333:                 });
sascha@333: 
sascha@333:                 double [][] vs2 = new double[2][vs[0].length];
sascha@333:                 for (int i = 0; i < indices.length; ++i) {
sascha@333:                     vs2[0][i] = vs[0][indices[i]];
sascha@337:                     vs2[1][i] = vs[1][indices[i]];
sascha@333:                 }
sascha@333:                 values.put(gaugeName, vs2);
sascha@333:             }
sascha@333:             else {
sascha@333:                 values.put(gaugeName, vs);
sascha@333:             }
sascha@333: 
sascha@146:         }
sascha@311: 
sascha@311:         return values;
sascha@146:     }
sascha@329: 
ingo@376:     public static double getQForW(double [][] values, double w) {
sascha@378: 
sascha@378:         boolean debug = log.isDebugEnabled();
sascha@378: 
sascha@378:         if (debug) {
sascha@378:             log.debug("calculating getQForW(" + w + ")");
sascha@378:         }
sascha@378: 
sascha@735:         int index = Arrays.binarySearch(values[1], w);
sascha@329:         if (index >= 0) {
sascha@334:             return values[0][index];
sascha@329:         }
sascha@329: 
sascha@329:         index = -index - 1; // insert position
sascha@329: 
sascha@378:         if (index < 1 || index >= values[0].length) {
sascha@329:             // do not extraploate
sascha@378:             if (debug) {
sascha@378:                 log.debug("we do not extrapolate: NaN");
sascha@378:             }
sascha@334:             return Double.NaN;
sascha@329:         }
sascha@329: 
sascha@735:         double w1 = values[1][index-1];
sascha@735:         double w2 = values[1][index  ];
sascha@735:         double q1 = values[0][index-1];
sascha@735:         double q2 = values[0][index  ];
sascha@329: 
sascha@334:         // q1 = m*w1 + b
sascha@334:         // q2 = m*w2 + b
sascha@334:         // q2 - q1 = m*(w2 - w1)
sascha@334:         // m = (q2 - q1)/(w2 - w1) # w2 != w1
sascha@334:         // b = q1 - m*w1
sascha@329: 
sascha@378:         double q;
sascha@334:         if (w1 == w2) {
sascha@378:             q = 0.5*(q1 + q2);
sascha@378:             if (debug) {
sascha@378:                 log.debug("same w1 and w1: " + w1);
sascha@378:             }
sascha@329:         }
sascha@378:         else {
sascha@378:             double m = (q2 - q1)/(w2 - w1);
sascha@378:             double b = q1 - m*w1;
sascha@378:             q = w*m + b;
sascha@378:         }
sascha@378:         if (debug) {
sascha@378:             log.debug("Q(" + w + ") = " + q);
sascha@378:         }
sascha@378:         return q;
sascha@329:     }
sascha@146: }
sascha@146: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :