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.exports; sascha@729: sascha@729: import java.io.IOException; sascha@729: import java.io.Writer; sascha@729: import java.io.PrintWriter; rrenkert@4938: import java.math.BigDecimal; sascha@729: rrenkert@4938: import java.text.DateFormat; rrenkert@4938: import java.util.Date; sascha@729: import java.util.Locale; sascha@729: teichmann@5831: import org.dive4elements.artifacts.CallMeta; ingo@1658: teichmann@5831: import org.dive4elements.river.artifacts.model.WQ; teichmann@5831: import org.dive4elements.river.artifacts.resources.Resources; sascha@729: sascha@732: import org.apache.commons.math.analysis.UnivariateRealFunction; sascha@729: sascha@732: import org.apache.commons.math.analysis.interpolation.SplineInterpolator; sascha@732: import org.apache.commons.math.analysis.interpolation.LinearInterpolator; sascha@729: sascha@732: import org.apache.commons.math.analysis.polynomials.PolynomialFunction; sascha@732: sascha@732: import org.apache.commons.math.FunctionEvaluationException; sascha@729: sascha@729: import org.apache.log4j.Logger; sascha@729: felix@5107: /** Write AT files. */ sascha@729: public class ATWriter sascha@729: { sascha@729: private static Logger logger = Logger.getLogger(ATWriter.class); sascha@729: sascha@729: public static final int COLUMNS = 10; sascha@729: ingo@1658: public static final String I18N_AT_HEADER = ingo@1658: "export.discharge.curve.at.header"; ingo@1658: rrenkert@4941: public static final String I18N_AT_GAUGE_HEADER = rrenkert@4941: "export.discharge.curve.at.gauge.header"; rrenkert@4941: aheinecke@6304: public static final String I18N_AT_CALC_GAUGE_HEADER = aheinecke@6304: "export.discharge.curve.at.gauge.calc.header"; aheinecke@6304: sascha@732: public static final String EMPTY = " "; sascha@729: aheinecke@6304: public static double getQ(int w, UnivariateRealFunction qFunc) { aheinecke@6304: try { aheinecke@6304: double val = qFunc.value(w); aheinecke@6304: return val; aheinecke@6304: } aheinecke@6304: catch (FunctionEvaluationException aode) { aheinecke@6304: // should not happen aheinecke@6304: logger.error("spline interpolation failed", aode); aheinecke@6304: return Double.NaN; aheinecke@6304: } sascha@729: } sascha@729: aheinecke@6304: public static void printQ(PrintWriter out, double q) { aheinecke@6304: String format; aheinecke@6304: if (q < 1d) format = " % 8.3f"; aheinecke@6304: else if (q < 10d) format = " % 8.2f"; aheinecke@6304: else if (q < 100d) format = " % 8.1f"; aheinecke@6304: else { aheinecke@6304: format = " % 8.0f"; aheinecke@6304: if (q > 1000d) q = Math.rint(q/10d)*10d; aheinecke@6304: } aheinecke@6304: out.printf(Locale.US, format, q); aheinecke@6304: } aheinecke@6304: aheinecke@6304: protected static void printCalculatedGaugeHeader( aheinecke@6304: PrintWriter out, aheinecke@6304: CallMeta callMeta, aheinecke@6304: String river, aheinecke@6304: double km, aheinecke@6304: String gName, aheinecke@6304: BigDecimal datum, aheinecke@6304: Date date, aheinecke@6304: String unit aheinecke@6304: ) { aheinecke@6304: out.print("*" + Resources.getMsg( aheinecke@6304: callMeta, aheinecke@6304: I18N_AT_CALC_GAUGE_HEADER, aheinecke@6304: I18N_AT_CALC_GAUGE_HEADER, aheinecke@6304: new Object[] { river, gName, datum, unit } )); aheinecke@6304: out.print("\r\n"); aheinecke@6304: } aheinecke@6304: aheinecke@6304: protected static void printGaugeHeader( aheinecke@6304: PrintWriter out, aheinecke@6304: CallMeta callMeta, aheinecke@6304: String river, aheinecke@6304: double km, aheinecke@6304: String gName, aheinecke@6304: BigDecimal datum, aheinecke@6304: Date date, aheinecke@6304: String unit aheinecke@6304: ) { aheinecke@6304: DateFormat f = DateFormat.getDateInstance(DateFormat.MEDIUM, aheinecke@6304: Resources.getLocale(callMeta)); aheinecke@6304: out.print("*" + Resources.getMsg( aheinecke@6304: callMeta, aheinecke@6304: I18N_AT_GAUGE_HEADER, aheinecke@6304: I18N_AT_GAUGE_HEADER, aheinecke@6304: new Object[] { river, gName, f.format(date), datum, unit} )); aheinecke@6304: out.print("\r\n"); aheinecke@6304: } aheinecke@6304: aheinecke@6304: protected static void printHeader( aheinecke@6304: PrintWriter out, aheinecke@6304: CallMeta callMeta, aheinecke@6304: String river, aheinecke@6304: double km aheinecke@6304: ) { aheinecke@6304: out.print("*" + Resources.getMsg( aheinecke@6304: callMeta, aheinecke@6304: I18N_AT_HEADER, aheinecke@6304: I18N_AT_HEADER, aheinecke@6304: new Object[] { river, km } )); aheinecke@6304: out.print("\r\n"); aheinecke@6304: } aheinecke@6304: aheinecke@6304: public static void write( aheinecke@6304: WQ values, aheinecke@6304: Writer writer, aheinecke@6304: CallMeta meta, aheinecke@6304: String river, aheinecke@6304: double km, aheinecke@6304: String gName, aheinecke@6304: BigDecimal datum, aheinecke@6304: Date date, aheinecke@6304: String unit) aheinecke@6304: throws IOException aheinecke@6304: { aheinecke@6304: int minW; aheinecke@6304: int maxW; aheinecke@6304: double minQ; aheinecke@6304: double maxQ; aheinecke@6304: aheinecke@6304: UnivariateRealFunction qFunc; aheinecke@6304: aheinecke@6304: WQ wq = WQ.getFixedWQforExportAtGauge(values, datum); aheinecke@6304: aheinecke@6304: // If we converted to centimeter we know that the WQ table is aheinecke@6304: // calculated because of the assumption that all calculations aheinecke@6304: // are in Meter and only the discharge tables data is in meter. aheinecke@6304: boolean isCalculation = wq.getReferenceSystem() != values.getReferenceSystem(); sascha@729: sascha@1033: int [] bounds = wq.longestIncreasingWRangeIndices(); sascha@729: sascha@1033: if (logger.isDebugEnabled()) { aheinecke@6304: logger.debug("exporting " + (isCalculation ? "calculated " : "") + aheinecke@6304: "w between indices " + bounds[0] + " and " + bounds[1] + " (" + aheinecke@6304: (int)Math.ceil(wq.getW(bounds[0])) + ", " + aheinecke@6304: (int)Math.floor(wq.getW(bounds[1]))+ ")"); sascha@1033: } sascha@1033: sascha@1033: if (bounds[1]-bounds[0] < 1) { // Only first w can be written out. aheinecke@6304: minW = maxW = (int)Math.round(wq.getW(bounds[0])); sascha@1033: minQ = maxQ = wq.getQ(bounds[0]); sascha@732: // constant function sascha@732: qFunc = new PolynomialFunction(new double [] { minQ }); sascha@729: return; sascha@729: } sascha@729: aheinecke@7605: /* example: bounds[0] = 0 aheinecke@7605: * bounds[1] = 5 -> we need to store 6 values.*/ aheinecke@7605: aheinecke@7605: double [] ws = new double[bounds[1]-bounds[0] + 1]; sascha@729: double [] qs = new double[ws.length]; sascha@729: aheinecke@7605: for (int i = 0; i < ws.length; i++) { sascha@1033: int idx = bounds[0]+i; sascha@1033: ws[i] = wq.getW(idx); sascha@1033: qs[i] = wq.getQ(idx); sascha@729: } sascha@729: sascha@732: qFunc = ws.length < 3 sascha@732: ? new LinearInterpolator().interpolate(ws, qs) sascha@732: : new SplineInterpolator().interpolate(ws, qs); sascha@729: aheinecke@6304: minW = (int)Math.ceil(wq.getW(bounds[0])); aheinecke@6304: maxW = (int)Math.floor(wq.getW(bounds[1])); sascha@1033: minQ = wq.getQ(bounds[0]); sascha@1033: maxQ = wq.getQ(bounds[1]); sascha@729: PrintWriter out = new PrintWriter(writer); sascha@729: felix@5107: // A header is required, because the desktop version of FLYS will skip ingo@1658: // the first row. rrenkert@4941: if (gName != null) { aheinecke@6304: if (isCalculation) { aheinecke@6304: printCalculatedGaugeHeader(out, meta, river, km, gName, datum, date, unit); aheinecke@6310: } else { aheinecke@6310: printGaugeHeader(out, meta, river, km, gName, datum, date, unit); aheinecke@6304: } rrenkert@4941: } rrenkert@4941: else { rrenkert@4941: printHeader(out, meta, river, km); rrenkert@4941: } rrenkert@4911: aheinecke@6304: int rest = minW % 10; sascha@729: aheinecke@6304: int startW = minW - rest; sascha@1682: sascha@1682: if (logger.isDebugEnabled()) { sascha@1682: logger.debug("startW: " + startW); sascha@1682: logger.debug("rest: " + rest); aheinecke@6304: logger.debug("maxW: " + maxW); sascha@729: } sascha@729: sascha@729: int col = 0; aheinecke@7605: for (int w = startW; w <= maxW; w++) { sascha@732: if (col == 0) { aheinecke@6304: out.printf(Locale.US, "%8d", w); sascha@732: } sascha@729: sascha@1682: if (w < minW) { sascha@1682: out.print(EMPTY); aheinecke@6304: } else { aheinecke@6304: double actQ = getQ(w, qFunc); aheinecke@6304: if (Double.isNaN(actQ)) { aheinecke@6304: // Can't happen™ aheinecke@6304: break; aheinecke@6304: } else { aheinecke@6304: printQ(out, actQ); aheinecke@6304: } sascha@1682: } sascha@729: sascha@729: if (++col >= COLUMNS) { teichmann@4947: out.print("\r\n"); sascha@729: col = 0; sascha@729: } sascha@729: } sascha@729: sascha@729: if (col > 0) { teichmann@4947: out.print("\r\n"); sascha@729: } sascha@729: sascha@729: out.flush(); sascha@729: } sascha@729: } sascha@729: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :