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 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 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 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 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 getValues() { ingo@4138: return getValues(MASTER_SCALE); sascha@328: } sascha@328: ingo@153: public Map 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 loadValues(double scale) { sascha@150: Map values = new HashMap(); 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 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 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 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: } sascha@146: } sascha@146: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :