teichmann@5863: /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde teichmann@5863: * Software engineering by Intevation GmbH teichmann@5863: * teichmann@5863: * This file is Free Software under the GNU AGPL (>=v3) teichmann@5863: * and comes with ABSOLUTELY NO WARRANTY! Check out the teichmann@5863: * documentation coming with Dive4Elements River for details. teichmann@5863: */ teichmann@5863: teichmann@5831: package org.dive4elements.river.artifacts.model; ingo@2215: ingo@2228: import java.util.ArrayList; ingo@4241: import java.util.Arrays; ingo@2228: import java.util.Date; ingo@2228: import java.util.List; ingo@2228: ingo@2215: import org.apache.log4j.Logger; ingo@2215: teichmann@5831: import org.dive4elements.river.model.DischargeTable; teichmann@5831: import org.dive4elements.river.model.Gauge; teichmann@5831: import org.dive4elements.river.model.TimeInterval; ingo@2219: teichmann@5914: import org.dive4elements.river.artifacts.access.HistoricalDischargeAccess; teichmann@5914: import org.dive4elements.river.artifacts.access.HistoricalDischargeAccess.EvaluationMode; teichmann@5914: ingo@2215: ingo@2215: /** felix@5339: * Historical Discharge Calculation. ingo@2215: * @author Ingo Weinzierl ingo@2215: */ ingo@2215: public class Calculation6 extends Calculation { ingo@2215: teichmann@5914: private static final Logger log = Logger.getLogger(Calculation6.class); ingo@2215: teichmann@5914: private int mode; teichmann@5914: private long [] timerange; teichmann@5914: private double [] values; teichmann@5914: private Long officialGaugeNumber; ingo@2215: ingo@2219: ingo@4136: public static final double SCALE = 1d; ingo@2230: teichmann@5914: public Calculation6(HistoricalDischargeAccess access) { teichmann@5914: EvaluationMode mode = access.getEvaluationMode(); teichmann@5914: Timerange tr = access.getEvaluationTimerange(); teichmann@5914: double [] vs = mode != null && mode == EvaluationMode.W teichmann@5914: ? access.getWs() teichmann@5914: : access.getQs(); teichmann@5914: teichmann@5914: Long officialGaugeNumber = access.getOfficialGaugeNumber(); teichmann@5914: teichmann@5914: if (mode == null) { teichmann@5914: // TODO: i18n teichmann@5914: addProblem("hist.discharge.mode.not.set"); teichmann@5914: } teichmann@5914: if (tr == null) { teichmann@5914: // TODO: i18n teichmann@5914: addProblem("hist.discharge.time.interval.not.set"); teichmann@5914: } teichmann@5914: if (vs == null || vs.length == 0) { teichmann@5914: // TODO: i18n teichmann@5914: addProblem("hist.discharge.values.not.set"); teichmann@5914: } teichmann@5914: teichmann@5914: if (officialGaugeNumber == null) { teichmann@5914: // TODO: i18n teichmann@5914: addProblem("hist.discharge.reference.gauge.not.set"); teichmann@5914: } teichmann@5914: teichmann@5914: if (!hasProblems()) { teichmann@5914: set( teichmann@5914: mode.getMode(), teichmann@5914: new long [] { tr.getStart(), tr.getEnd()}, teichmann@5914: values, teichmann@5914: officialGaugeNumber); teichmann@5914: } teichmann@5914: } teichmann@5914: teichmann@5914: protected void set( teichmann@5914: int mode, teichmann@5914: long [] timerange, teichmann@5914: double [] values, teichmann@5914: Long officialGaugeNumber teichmann@5914: ) { teichmann@5914: this.mode = mode; teichmann@5914: this.timerange = timerange; teichmann@5914: this.values = values; teichmann@5914: this.officialGaugeNumber = officialGaugeNumber; teichmann@5914: } teichmann@5914: ingo@2228: public Calculation6(int mode, long[] timerange, double[] values) { ingo@4232: this.mode = mode; ingo@2219: this.timerange = timerange; ingo@4232: this.values = values; ingo@2215: } ingo@2215: teichmann@5914: public CalculationResult calculate() { teichmann@5914: if (hasProblems()) { teichmann@5914: log.warn("Parameters not valid for calculation."); ingo@2219: return null; ingo@2219: } ingo@2219: teichmann@5914: Gauge gauge = Gauge.getGaugeByOfficialNumber(officialGaugeNumber); teichmann@5914: if (gauge == null) { teichmann@5914: // TODO: i18n teichmann@5914: addProblem("hist.discharge.gauge.not.found"); teichmann@5914: return null; teichmann@5914: } teichmann@5914: teichmann@5914: if (log.isDebugEnabled()) { ingo@2219: debug(); ingo@2219: } ingo@2219: ingo@4232: DischargeTable refTable = fetchReferenceTable(gauge); ingo@2228: List dts = fetchDischargeTables(gauge); ingo@2219: ingo@2304: int numTables = dts.size(); ingo@2304: teichmann@5914: log.debug("Take " + numTables + " into account."); ingo@2304: ingo@2304: if (numTables == 0) { ingo@2304: addProblem("cannot.find.hist.q.tables"); ingo@2304: } ingo@2228: ingo@4241: WQTimerange[] wqt = prepareTimerangeData(refTable, dts); ingo@4241: WQKms[] wqs = prepareWQData(dts); ingo@2228: teichmann@5914: log.debug("Number of calculation results: " + wqt.length); ingo@2250: ingo@4241: return new CalculationResult(new HistoricalDischargeData(wqt, wqs), ingo@4241: this); ingo@2215: } ingo@2219: ingo@2311: protected DischargeTable fetchReferenceTable(Gauge gauge) { ingo@2311: return gauge.fetchMasterDischargeTable(); ingo@2311: } ingo@2311: ingo@2228: protected List fetchDischargeTables(Gauge gauge) { ingo@2228: List relevant = new ArrayList(); ingo@4232: List all = gauge.getDischargeTables(); ingo@2228: ingo@4232: for (DischargeTable dt : all) { ingo@2228: if (isDischargeTableRelevant(dt)) { ingo@2228: relevant.add(dt); ingo@2228: } ingo@2228: } ingo@2228: ingo@2228: return relevant; ingo@2228: } ingo@2228: felix@5339: /** True if timerange of given discharge table overlaps with timerange. */ ingo@2228: protected boolean isDischargeTableRelevant(DischargeTable dt) { ingo@2228: TimeInterval ti = dt.getTimeInterval(); ingo@2228: ingo@2598: if (dt.getKind() == Gauge.MASTER_DISCHARGE_TABLE || ti == null) { ingo@2315: return false; ingo@2315: } ingo@2315: ingo@4232: Date start = ti.getStartTime(); ingo@2228: long startTime = start.getTime(); ingo@2228: ingo@2228: if (startTime >= timerange[0] && startTime <= timerange[1]) { ingo@2228: return true; ingo@2228: } ingo@2228: ingo@4232: Date stop = ti.getStopTime(); ingo@2228: long stopTime = stop != null ? stop.getTime() : -1l; ingo@2228: ingo@2228: if (stopTime >= timerange[0] && stopTime <= timerange[1]) { ingo@2228: return true; ingo@2228: } ingo@2228: teichmann@5914: log.debug("DischargeTable not in range: " + start + " -> " + stop); ingo@2228: ingo@2228: return false; ingo@2228: } ingo@2228: ingo@4241: protected WQTimerange[] prepareTimerangeData(DischargeTable refTable, ingo@4232: List dts) { ingo@2311: if (refTable == null) { ingo@2311: addProblem("cannot.find.hist.q.reftable"); ingo@2311: return prepareSimpleData(dts); ingo@2311: } ingo@2311: else { ingo@2311: return prepareData(refTable, dts); ingo@2311: } ingo@2311: } ingo@2311: ingo@4241: protected WQKms[] prepareWQData(List dts) { ingo@4241: WQKms[] wqs = new WQKms[dts.size()]; ingo@4241: ingo@4241: int idx = 0; ingo@4241: ingo@4241: for (DischargeTable dt : dts) { ingo@4241: double[][] values = null; ingo@4241: ingo@4241: if (dt.getKind() == DischargeTables.MASTER) { ingo@4241: values = DischargeTables.loadDischargeTableValues(dt, ingo@4241: DischargeTables.MASTER_SCALE); ingo@4241: ingo@4241: } ingo@4241: else { ingo@4241: values = DischargeTables.loadDischargeTableValues(dt, ingo@4241: DischargeTables.HISTORICAL_SCALE); ingo@4241: ingo@4241: } ingo@4241: ingo@4241: wqs[idx++] = prepareWQ(dt, values); ingo@4241: } ingo@4241: ingo@4241: return wqs; ingo@4241: } ingo@4241: ingo@4241: protected WQKms prepareWQ(DischargeTable dt, double[][] values) { ingo@4241: double km = dt.getGauge().getStation().doubleValue(); ingo@4241: ingo@4241: double[] kms = new double[values[0].length]; ingo@4241: Arrays.fill(kms, km); ingo@4241: ingo@4281: return new HistoricalWQKms(kms, values[0], values[1], ingo@4241: String.valueOf(km), dt.getTimeInterval()); ingo@4241: } ingo@4241: felix@5339: /** Without reference. */ ingo@2311: protected WQTimerange[] prepareSimpleData(List dts) { ingo@4232: List wqts = new ArrayList(values.length); ingo@2228: ingo@4232: for (double value : values) { teichmann@5914: log.debug("Prepare data for value: " + value); ingo@2228: teichmann@5914: String name = mode == EvaluationMode.W.getMode() teichmann@5914: ? "W=" + value teichmann@5914: : "Q=" + value; teichmann@5914: ingo@4232: WQTimerange wqt = null; ingo@2228: ingo@4232: for (DischargeTable dt : dts) { ingo@4232: Date[] ti = prepareTimeInterval(dt); ingo@2315: Timerange t = new Timerange(ti[0], ti[1]); ingo@4232: double w; ingo@4232: double q; ingo@2228: teichmann@5914: if (mode == EvaluationMode.W.getMode()) { ingo@2228: w = value; ingo@4138: q = findValueForW(dt, w, DischargeTables.HISTORICAL_SCALE); ingo@2244: ingo@2244: if (Double.isNaN(q)) { teichmann@5914: log.warn("Cannot find Q for W: " + w); ingo@2315: addProblem("cannot.find.hist.q.for.w", w, ti[0], ti[1]); ingo@2244: continue; ingo@2244: } ingo@2228: } ingo@2228: else { ingo@2228: q = value; ingo@4232: w = findValueForQ(dt, q, DischargeTables.HISTORICAL_SCALE); ingo@2228: } ingo@2228: teichmann@5914: log.debug("Q=" + q + " | W=" + w); ingo@2230: ingo@2244: if (wqt == null) { ingo@2244: wqt = new WQTimerange(name); ingo@2244: } ingo@2244: ingo@2228: wqt.add(w, q, t); ingo@2228: } ingo@2228: ingo@2244: if (wqt != null) { ingo@2244: wqts.add(wqt); ingo@2244: } ingo@2228: } ingo@2228: sascha@3452: return wqts.toArray(new WQTimerange[wqts.size()]); ingo@2228: } ingo@2228: felix@5339: /** With reference. */ ingo@4232: protected HistoricalWQTimerange[] prepareData(DischargeTable refTable, ingo@4232: List dts) { ingo@4232: List wqts = new ArrayList( ingo@4232: values.length); ingo@2228: ingo@4232: for (double value : values) { teichmann@5914: log.debug("Prepare data plus diff for value: " + value); ingo@2311: teichmann@5914: String name = mode == EvaluationMode.W.getMode() teichmann@5914: ? "W=" + value teichmann@5914: : "Q=" + value; ingo@4232: HistoricalWQTimerange wqt = null; ingo@2311: ingo@2311: double ref; ingo@2315: double diff; ingo@2311: teichmann@5914: if (refTable != null && mode == EvaluationMode.W.getMode()) { ingo@4232: ref = findValueForW(refTable, value, ingo@4232: DischargeTables.MASTER_SCALE); ingo@2311: } ingo@2311: else if (refTable != null) { ingo@4232: ref = findValueForQ(refTable, value, ingo@4232: DischargeTables.MASTER_SCALE); ingo@2311: } ingo@2311: else { ingo@2311: ref = Double.NaN; ingo@2311: } ingo@2311: ingo@4232: for (DischargeTable dt : dts) { ingo@2315: Date[] ti = prepareTimeInterval(dt); ingo@2311: ingo@4232: Timerange t = new Timerange(ti[0], ti[1]); ingo@4232: double w; ingo@4232: double q; ingo@2311: teichmann@5914: if (mode == EvaluationMode.W.getMode()) { ingo@4232: w = value; ingo@4232: q = findValueForW(dt, w, DischargeTables.HISTORICAL_SCALE); ingo@2311: ingo@2311: if (Double.isNaN(q)) { teichmann@5914: log.warn("Cannot find Q for W: " + w); ingo@2315: addProblem("cannot.find.hist.q.for.w", w, ti[0], ti[1]); ingo@2311: continue; ingo@2311: } ingo@2315: ingo@4232: diff = ref - q; ingo@2311: } ingo@2311: else { ingo@4232: q = value; ingo@4232: w = findValueForQ(dt, q, DischargeTables.HISTORICAL_SCALE); ingo@4232: ingo@4232: if (Double.isNaN(w)) { teichmann@5914: log.warn("Cannot find W for Q: " + q); ingo@4232: addProblem("cannot.find.hist.w.for.q", q, ti[0], ti[1]); ingo@4232: continue; ingo@4232: } ingo@4232: diff = ref - w; ingo@2311: } ingo@2311: teichmann@5914: log.debug("Q=" + q + " | W=" + w + " | Ref = " + ref); ingo@2311: ingo@2311: if (wqt == null) { ingo@2311: wqt = new HistoricalWQTimerange(name); ingo@2311: } ingo@2311: ingo@2315: wqt.add(w, q, diff, t); ingo@2311: } ingo@2311: ingo@2311: if (wqt != null) { ingo@2311: wqts.add(wqt); ingo@2311: } ingo@2311: } ingo@2311: ingo@4232: return (HistoricalWQTimerange[]) wqts ingo@4232: .toArray(new HistoricalWQTimerange[wqts.size()]); ingo@2311: } ingo@2311: felix@5339: /** Returns discharge table interval as Date[]. */ ingo@2315: protected Date[] prepareTimeInterval(DischargeTable dt) { ingo@2315: TimeInterval ti = dt.getTimeInterval(); ingo@2315: ingo@2315: Date start = ti.getStartTime(); ingo@4232: Date end = ti.getStopTime(); ingo@2315: ingo@2315: if (end == null) { teichmann@5914: log.warn("TimeInterval has no stop time set!"); ingo@2315: ingo@2315: end = new Date(); ingo@2315: } ingo@2315: ingo@2315: return new Date[] { start, end }; ingo@2315: } ingo@2315: ingo@4138: protected double findValueForW(DischargeTable dt, double w, double scale) { ingo@4138: double[][] vs = DischargeTables.loadDischargeTableValues(dt, scale); ingo@4232: double[] qs = DischargeTables.getQsForW(vs, w); sascha@2418: return qs.length == 0 ? Double.NaN : qs[0]; ingo@2228: } ingo@2228: ingo@4232: protected double findValueForQ(DischargeTable dt, double q, double scale) { ingo@4232: double[][] vs = DischargeTables.loadDischargeTableValues(dt, scale); ingo@4232: double[] ws = DischargeTables.getWsForQ(vs, q); ingo@2228: ingo@4232: return ws.length == 0 ? Double.NaN : ws[0]; ingo@2228: } ingo@2228: ingo@2219: /** teichmann@5914: * Writes the parameters used for this calculation to log. ingo@2219: */ ingo@2219: public void debug() { teichmann@5914: log.debug("========== Calculation6 =========="); teichmann@5914: log.debug(" Mode: " + mode); teichmann@5914: log.debug(" Timerange: " + timerange[0] + " - " + timerange[1]); teichmann@5914: log.debug(" Input values: " + Arrays.toString(values)); teichmann@5914: log.debug("=================================="); ingo@2219: } ingo@2215: } ingo@2215: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :