Mercurial > dive4elements > river
diff flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/DischargeTables.java @ 5831:bd047b71ab37
Repaired internal references
author | Sascha L. Teichmann <teichmann@intevation.de> |
---|---|
date | Thu, 25 Apr 2013 12:06:39 +0200 |
parents | flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java@5f38d1c39ebd |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/DischargeTables.java Thu Apr 25 12:06:39 2013 +0200 @@ -0,0 +1,312 @@ +package org.dive4elements.river.artifacts.model; + +import java.util.List; +import java.util.Map; +import java.util.HashMap; +import java.util.Arrays; + +import java.io.Serializable; + +import org.hibernate.Session; +import org.hibernate.Query; + +import org.apache.log4j.Logger; + +import org.dive4elements.river.backend.SessionHolder; +import org.dive4elements.river.model.Gauge; +import org.dive4elements.river.model.DischargeTable; +import org.dive4elements.river.model.DischargeTableValue; + +import gnu.trove.TDoubleArrayList; + +/** Documentation goes here. */ +public class DischargeTables +implements Serializable +{ + /** Private logger. */ + private static Logger log = Logger.getLogger(DischargeTables.class); + + /** Scale to convert discharge table values of master table into [cm]. */ + public static final double MASTER_SCALE = 100d; + + /** Scale to convert discharge table values of historical tables into [cm]. */ + public static final double HISTORICAL_SCALE = 1d; + + public static final int MASTER = 0; + + protected List<String> gaugeNames; + + protected String riverName; + + protected double scale; + + protected int kind; + + protected Map<String, double [][]> values; + + public DischargeTables() { + } + + public DischargeTables(String riverName, String gaugeName) { + this(riverName, gaugeName, MASTER); + } + + public DischargeTables(String riverName, String gaugeName, int kind) { + this(riverName, new String [] { gaugeName }, kind); + } + + public DischargeTables(String riverName, String [] gaugeNames) { + this(riverName, gaugeNames, MASTER); + } + + public DischargeTables(String riverName, String [] gaugeNames, int kind) { + this(riverName, Arrays.asList(gaugeNames), kind); + } + + public DischargeTables( + String riverName, + List<String> gaugeNames, + int kind + ) { + scale = Double.NaN; + this.kind = kind; + this.riverName = riverName; + this.gaugeNames = gaugeNames; + } + + public double [][] getFirstTable() { + return getFirstTable(MASTER_SCALE); + } + + public double [][] getFirstTable(double scale) { + Map<String, double [][]> values = getValues(scale); + for (double [][] table: values.values()) { + return table; + } + return null; + } + + public Map<String, double [][]> getValues() { + return getValues(MASTER_SCALE); + } + + public Map<String, double [][]> getValues(double scale) { + if (values == null || scale != this.scale) { + values = loadValues(scale); + this.scale = scale; + } + return values; + } + + /** + * Returns mapping of gauge name to values. + */ + protected Map<String, double [][]> loadValues(double scale) { + Map<String, double [][]> values = new HashMap<String, double [][]>(); + + Session session = SessionHolder.HOLDER.get(); + + Query gaugeQuery = session.createQuery( + "from Gauge where name=:gauge and river.name=:river"); + gaugeQuery.setParameter("river", riverName); + + for (String gaugeName: gaugeNames) { + gaugeQuery.setParameter("gauge", gaugeName); + List<Gauge> gauges = gaugeQuery.list(); + if (gauges.isEmpty()) { + log.warn( + "no gauge '"+gaugeName+"' at river '"+riverName+"'"); + continue; + } + Gauge gauge = gauges.get(0); + + List<DischargeTable> tables = gauge.getDischargeTables(); + + if (tables.isEmpty()) { + log.warn( + "no discharge table for gauge '" + gaugeName + "'"); + continue; + } + + // TODO: Filter by time interval + DischargeTable table = null; + for (DischargeTable dt : tables) { + if (dt.getKind() == 0) { + table = dt; + break; + } + } + if (table == null) { + table = tables.get(0); + } + double [][] vs = loadDischargeTableValues(table, scale); + + values.put(gaugeName, vs); + } + + return values; + } + + + /** + * @param table The discharge table + * @param scale The scale factor to adjust W and Q values. + * + * @return the values of a discharge table. + */ + public static double[][] loadDischargeTableValues( + DischargeTable table, + double scale + ) { + List<DischargeTableValue> dtvs = table.getDischargeTableValues(); + + final double [][] vs = new double[2][dtvs.size()]; + + int idx = 0; + for (DischargeTableValue dtv: dtvs) { + double q = dtv.getQ().doubleValue(); + vs[0][idx] = q * scale; + vs[1][idx] = dtv.getW().doubleValue() * scale; + ++idx; + } + + return vs; + } + + private static final double EPSILON = 1e-5; + + private static final boolean epsEquals(double a, double b) { + return Math.abs(a - b) < EPSILON; + } + + private static final boolean between(double a, double b, double x) { + if (a > b) { double t = a; a = b; b = t; } + return x > a && x < b; + } + + /** + * Find or interpolate Qs from q/w array. + * @param values [[q0,q1,q2],[w0,w1,w2]] + * @param w W value to look for in values. + */ + public static double [] getQsForW(double [][] values, double w) { + + boolean debug = log.isDebugEnabled(); + + if (debug) { + log.debug("getQsForW: W = " + w); + } + + double [] qs = values[0]; + double [] ws = values[1]; + + int N = Math.min(qs.length, ws.length); + + if (N == 0) { + if (debug) { + log.debug("Q(" + w + ") = []"); + } + return new double [0]; + } + + TDoubleArrayList outQs = new TDoubleArrayList(); + + if (epsEquals(ws[0], w)) { + outQs.add(qs[0]); + } + + for (int i = 1; i < N; ++i) { + if (epsEquals(ws[i], w)) { + outQs.add(qs[i]); + } + else if (between(ws[i-1], ws[i], w)) { + double w1 = ws[i-1]; + double w2 = ws[i]; + double q1 = qs[i-1]; + double q2 = qs[i]; + + // q1 = m*w1 + b + // q2 = m*w2 + b + // q2 - q1 = m*(w2 - w1) + // m = (q2 - q1)/(w2 - w1) # w2 != w1 + // b = q1 - m*w1 + // w1 != w2 + + double m = (q2 - q1)/(w2 - w1); + double b = q1 - m*w1; + double q = w*m + b; + + outQs.add(q); + } + } + + double [] result = outQs.toNativeArray(); + + if (debug) { + log.debug("Q(" + w + ") = " + Arrays.toString(result)); + } + + return result; + } + + public static double [] getWsForQ(double [][] values, double q) { + + boolean debug = log.isDebugEnabled(); + + if (debug) { + log.debug("getWsForQ: W = " + q); + } + + double [] qs = values[0]; + double [] ws = values[1]; + + int N = Math.min(qs.length, ws.length); + + if (N == 0) { + if (debug) { + log.debug("W(" + q + ") = []"); + } + return new double [0]; + } + + TDoubleArrayList outWs = new TDoubleArrayList(); + + if (epsEquals(qs[0], q)) { + outWs.add(ws[0]); + } + + for (int i = 1; i < N; ++i) { + if (epsEquals(qs[i], q)) { + outWs.add(ws[i]); + } + else if (between(qs[i-1], qs[i], q)) { + double w1 = ws[i-1]; + double w2 = ws[i]; + double q1 = qs[i-1]; + double q2 = qs[i]; + + // w1 = m*q1 + b + // w2 = m*q2 + b + // w2 - w1 = m*(q2 - q1) + // m = (w2 - w1)/(q2 - q1) # q2 != q1 + // b = w1 - m*q1 + // q1 != q2 + + double m = (w2 - w1)/(q2 - q1); + double b = w1 - m*q1; + double w = q*m + b; + + outWs.add(w); + } + } + + double [] result = outWs.toNativeArray(); + + if (debug) { + log.debug("W(" + q + ") = " + Arrays.toString(result)); + } + + return result; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :