teichmann@5863: /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
teichmann@5863: * Software engineering by Intevation GmbH
teichmann@5863: *
teichmann@5994: * This file is Free Software under the GNU AGPL (>=v3)
teichmann@5863: * and comes with ABSOLUTELY NO WARRANTY! Check out the
teichmann@5994: * 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;
teichmann@5962: import java.util.HashMap;
ingo@2228: import java.util.List;
teichmann@5962: import java.util.Map;
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:
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@5994: mode.getMode(),
teichmann@5914: new long [] { tr.getStart(), tr.getEnd()},
teichmann@5918: vs,
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:
teichmann@5962: protected CalculationResult error(String msg) {
teichmann@5962: addProblem(msg);
teichmann@5962: return new CalculationResult(new HistoricalDischargeData(), this);
teichmann@5962: }
teichmann@5962:
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@5962: return error("hist.discharge.gauge.not.found");
teichmann@5914: }
teichmann@5914:
teichmann@5914: if (log.isDebugEnabled()) {
ingo@2219: debug();
ingo@2219: }
ingo@2219:
ingo@2228: List dts = fetchDischargeTables(gauge);
teichmann@5962: if (dts.isEmpty()) {
teichmann@5962: return error("cannot.find.hist.q.tables");
ingo@2304: }
ingo@2228:
teichmann@5962: DischargeTable refTable = fetchReferenceTable(dts);
ingo@2228:
teichmann@5962: boolean debug = log.isDebugEnabled();
teichmann@5962:
teichmann@5962: if (debug) {
teichmann@5962: log.debug("Take " + dts.size() + " into account.");
teichmann@5962: }
teichmann@5962:
teichmann@5962: ValuesCache vc = new ValuesCache();
teichmann@5962:
teichmann@5962: WQTimerange [] wqt = prepareData(refTable, dts, vc);
teichmann@5962: WQKms [] wqs = prepareWQData(dts, vc);
teichmann@5962:
teichmann@5962: if (debug) {
teichmann@5962: log.debug("Number of calculation results: " + wqt.length);
teichmann@5962: }
ingo@2250:
ingo@4241: return new CalculationResult(new HistoricalDischargeData(wqt, wqs),
ingo@4241: this);
ingo@2215: }
ingo@2219:
teichmann@5962: /** The youngest discharge table of the selected set is the reference */
teichmann@5962: protected DischargeTable fetchReferenceTable(List dts) {
teichmann@5962: DischargeTable ref = null;
teichmann@5962: long now = System.currentTimeMillis();
teichmann@5962: for (DischargeTable dt: dts) {
teichmann@5962: if (ref == null) {
teichmann@5962: ref = dt;
teichmann@5962: }
teichmann@5962: else {
teichmann@5962: TimeInterval cti = dt.getTimeInterval();
teichmann@5962: TimeInterval rti = ref.getTimeInterval();
teichmann@5962:
teichmann@5962: long ct = cti.getStopTime() != null
teichmann@5962: ? cti.getStopTime().getTime()
teichmann@5962: : now;
teichmann@5962: long rt = rti.getStopTime() != null
teichmann@5962: ? rti.getStopTime().getTime()
teichmann@5962: : now;
teichmann@5962:
teichmann@5962: if (ct > rt) {
teichmann@5962: ref = dt;
teichmann@5962: }
teichmann@5962:
teichmann@5962: }
teichmann@5962: }
teichmann@5962: return ref;
ingo@2311: }
ingo@2311:
ingo@2228: protected List fetchDischargeTables(Gauge gauge) {
teichmann@5962:
ingo@4232: List all = gauge.getDischargeTables();
teichmann@5962: List relevant = new ArrayList(all.size());
ingo@2228:
teichmann@5962: 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) {
teichmann@5962:
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:
teichmann@5962: long dtStart = ti.getStartTime().getTime();
teichmann@5962: long dtStop = ti.getStopTime() != null
teichmann@5962: ? ti.getStopTime().getTime()
teichmann@5962: : System.currentTimeMillis();
ingo@2228:
teichmann@5962: return !(timerange[1] < dtStart || timerange[0] > dtStop);
ingo@2228: }
ingo@2228:
teichmann@5962: protected WQKms[] prepareWQData(List dts, ValuesCache vc) {
ingo@4241: WQKms[] wqs = new WQKms[dts.size()];
ingo@4241:
teichmann@5962: for (int i = 0, N = wqs.length; i < N; ++i) {
teichmann@5962: DischargeTable dt = dts.get(i);
teichmann@5962: wqs[i] = prepareWQ(dt, vc.getValues(dts.get(i)));
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:
teichmann@5962: protected String name(double value) {
teichmann@5962: return mode == EvaluationMode.W.getMode()
teichmann@5962: ? "W=" + value
teichmann@5962: : "Q=" + value;
teichmann@5962: }
ingo@2228:
teichmann@5962: /** With reference. */
teichmann@5962: protected HistoricalWQTimerange[] prepareData(
teichmann@5962: DischargeTable refTable,
teichmann@5962: List dts,
teichmann@5962: ValuesCache vc
teichmann@5962: ) {
teichmann@5962: List wqts =
teichmann@5962: new ArrayList(values.length);
ingo@2228:
teichmann@5962: boolean debug = log.isDebugEnabled();
teichmann@5914:
teichmann@5962: for (double value: values) {
teichmann@5962: if (debug) {
teichmann@5962: log.debug("Prepare data plus diff for value: " + value);
teichmann@5962: }
teichmann@5962:
teichmann@5962: double ref = mode == EvaluationMode.W.getMode()
teichmann@5962: ? vc.findValueForW(refTable, value)
teichmann@5962: : vc.findValueForQ(refTable, value);
teichmann@5962:
teichmann@5962: if (Double.isNaN(ref)) {
teichmann@6064: addProblem("hist.discharge.no.value.in.ref", value);
teichmann@5962: continue;
teichmann@5962: }
teichmann@5962:
teichmann@5962: String name = name(value);
teichmann@5962: HistoricalWQTimerange 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;
teichmann@5962: double diff;
ingo@2228:
teichmann@5914: if (mode == EvaluationMode.W.getMode()) {
teichmann@5962: q = vc.findValueForW(dt, w = value);
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 {
teichmann@5962: w = vc.findValueForQ(dt, q = value);
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@5962: if (debug) {
teichmann@5962: log.debug("Q=" + q + " | W=" + w + " | Ref = " + ref);
teichmann@5962: }
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:
teichmann@5962: return (HistoricalWQTimerange[])wqts.toArray(
teichmann@5962: 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@2228:
teichmann@5962: /** Helper to avoid redundant loading of discharge table values. */
teichmann@5962: private static final class ValuesCache {
ingo@2228:
teichmann@5962: private Map cache;
teichmann@5962:
teichmann@5962: ValuesCache() {
teichmann@5962: cache = new HashMap();
teichmann@5962: }
teichmann@5962:
teichmann@5962: double [][] getValues(DischargeTable dt) {
teichmann@5962: Integer id = dt.getId();
teichmann@5962: double [][] vs = cache.get(id);
teichmann@5962: if (vs == null) {
aheinecke@6301: vs = DischargeTables.loadDischargeTableValues(dt);
teichmann@5962: cache.put(id, vs);
teichmann@5962: }
teichmann@5962: return vs;
teichmann@5962: }
teichmann@5962:
teichmann@5962: private static double firstOrNaN(double [] vs) {
teichmann@5962: return vs.length > 0 ? vs[0] : Double.NaN;
teichmann@5962: }
teichmann@5962:
teichmann@5962: double findValueForW(DischargeTable dt, double w) {
teichmann@5962: return firstOrNaN(DischargeTables.getQsForW(getValues(dt), w));
teichmann@5962: }
teichmann@5962:
teichmann@5962: double findValueForQ(DischargeTable dt, double q) {
teichmann@5962: return firstOrNaN(DischargeTables.getWsForQ(getValues(dt), q));
teichmann@5962: }
teichmann@5962: } // class ValuesCache
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 :