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;
ingo@389:
ingo@446: import java.io.IOException;
ingo@389: import java.io.OutputStream;
ingo@2045: import java.text.DateFormat;
ingo@418: import java.text.NumberFormat;
ingo@389: import java.util.ArrayList;
raimund@2176: import java.util.Map;
raimund@2176: import java.util.HashMap;
ingo@2045: import java.util.Date;
ingo@389: import java.util.List;
ingo@2045: import java.util.Locale;
ingo@2035: import java.util.regex.Matcher;
ingo@2035: import java.util.regex.Pattern;
ingo@389:
ingo@389: import org.w3c.dom.Document;
ingo@389:
ingo@389: import org.apache.log4j.Logger;
ingo@389:
ingo@389: import au.com.bytecode.opencsv.CSVWriter;
ingo@389:
teichmann@5831: import org.dive4elements.river.artifacts.model.ConstantWQKms;
teichmann@4836:
raimund@2176: import net.sf.jasperreports.engine.JasperExportManager;
raimund@2176: import net.sf.jasperreports.engine.JasperFillManager;
raimund@2176: import net.sf.jasperreports.engine.JasperPrint;
raimund@2176: import net.sf.jasperreports.engine.JRException;
raimund@2176:
aheinecke@6601: import org.dive4elements.artifacts.Artifact;
teichmann@5831: import org.dive4elements.artifacts.CallContext;
teichmann@5831: import org.dive4elements.artifacts.CallMeta;
teichmann@5831: import org.dive4elements.artifacts.common.utils.Config;
ingo@2066:
teichmann@5831: import org.dive4elements.river.model.Gauge;
sascha@706:
teichmann@5831: import org.dive4elements.river.artifacts.access.FixRealizingAccess;
teichmann@5831: import org.dive4elements.river.artifacts.access.RangeAccess;
teichmann@5831: import org.dive4elements.river.artifacts.FixationArtifact;
teichmann@5867: import org.dive4elements.river.artifacts.D4EArtifact;
teichmann@5831: import org.dive4elements.river.artifacts.WINFOArtifact;
aheinecke@6601: import org.dive4elements.river.artifacts.StaticWQKmsArtifact;
teichmann@5831: import org.dive4elements.river.artifacts.model.CalculationResult;
teichmann@5831: import org.dive4elements.river.artifacts.model.Segment;
teichmann@5831: import org.dive4elements.river.artifacts.model.WQCKms;
teichmann@5831: import org.dive4elements.river.artifacts.model.WQKms;
teichmann@5831: import org.dive4elements.river.artifacts.model.WKmsJRDataSource;
teichmann@5831: import org.dive4elements.river.artifacts.model.WQKmsResult;
teichmann@5831: import org.dive4elements.river.artifacts.resources.Resources;
teichmann@5831:
teichmann@5865: import org.dive4elements.river.utils.RiverUtils;
teichmann@5865: import org.dive4elements.river.utils.RiverUtils.WQ_MODE;
teichmann@5831: import org.dive4elements.river.utils.Formatter;
ingo@389:
ingo@389: /**
felix@3252: * Generates different output formats (wst, csv, pdf) of data that resulted from
felix@3252: * a waterlevel computation.
felix@3252: *
ingo@389: * @author Ingo Weinzierl
ingo@389: */
ingo@391: public class WaterlevelExporter extends AbstractExporter {
ingo@389:
ingo@389: /** The logger used in this exporter.*/
ingo@389: private static Logger logger = Logger.getLogger(WaterlevelExporter.class);
ingo@389:
ingo@446: public static final String FACET_WST = "wst";
ingo@446:
aheinecke@6601: /* This should be the same as in the StaticWQKmsArtifact */
aheinecke@6601: public static final String STATICWQKMSNAME = "staticwqkms";
aheinecke@6601:
ingo@416: public static final String CSV_KM_HEADER =
ingo@416: "export.waterlevel.csv.header.km";
ingo@416:
ingo@416: public static final String CSV_W_HEADER =
ingo@416: "export.waterlevel.csv.header.w";
ingo@416:
ingo@416: public static final String CSV_Q_HEADER =
ingo@416: "export.waterlevel.csv.header.q";
ingo@416:
ingo@2063: public static final String CSV_Q_DESC_HEADER =
ingo@2063: "export.waterlevel.csv.header.q.desc";
ingo@2063:
ingo@2068: public static final String CSV_W_DESC_HEADER =
ingo@2068: "export.waterlevel.csv.header.w.desc";
ingo@2068:
ingo@2063: public static final String CSV_LOCATION_HEADER =
ingo@2063: "export.waterlevel.csv.header.location";
ingo@2063:
ingo@2063: public static final String CSV_GAUGE_HEADER =
ingo@2063: "export.waterlevel.csv.header.gauge";
ingo@2063:
ingo@2045: public static final String CSV_META_RESULT =
ingo@2045: "export.waterlevel.csv.meta.result";
ingo@2045:
ingo@2045: public static final String CSV_META_CREATION =
ingo@2045: "export.waterlevel.csv.meta.creation";
ingo@2045:
ingo@2045: public static final String CSV_META_CALCULATIONBASE =
ingo@2045: "export.waterlevel.csv.meta.calculationbase";
ingo@2045:
ingo@2045: public static final String CSV_META_RIVER =
ingo@2045: "export.waterlevel.csv.meta.river";
ingo@2045:
ingo@2045: public static final String CSV_META_RANGE =
ingo@2045: "export.waterlevel.csv.meta.range";
ingo@2045:
ingo@2045: public static final String CSV_META_GAUGE =
ingo@2045: "export.waterlevel.csv.meta.gauge";
ingo@2045:
ingo@2045: public static final String CSV_META_Q =
ingo@2045: "export.waterlevel.csv.meta.q";
ingo@2045:
ingo@2045: public static final String CSV_META_W =
ingo@2045: "export.waterlevel.csv.meta.w";
ingo@2045:
ingo@2066: public static final String CSV_NOT_IN_GAUGE_RANGE =
ingo@2066: "export.waterlevel.csv.not.in.gauge.range";
ingo@2066:
ingo@2035: public static final Pattern NUMBERS_PATTERN =
ingo@2035: Pattern.compile("\\D*(\\d++.\\d*)\\D*");
ingo@2035:
ingo@2063: public static final String DEFAULT_CSV_KM_HEADER = "Fluss-Km";
ingo@2063: public static final String DEFAULT_CSV_W_HEADER = "W [NN + m]";
ingo@2063: public static final String DEFAULT_CSV_Q_HEADER = "Q [m\u00b3/s]";
ingo@2063: public static final String DEFAULT_CSV_Q_DESC_HEADER = "Bezeichnung";
ingo@2068: public static final String DEFAULT_CSV_W_DESC_HEADER = "W/Pegel [cm]";
ingo@2063: public static final String DEFAULT_CSV_LOCATION_HEADER = "Lage";
ingo@2063: public static final String DEFAULT_CSV_GAUGE_HEADER = "Bezugspegel";
ingo@2066: public static final String DEFAULT_CSV_NOT_IN_GAUGE_RANGE =
ingo@2066: "außerhalb des gewählten Bezugspegels";
ingo@416:
raimund@2176: public static final String PDF_HEADER_MODE = "export.waterlevel.pdf.mode";
felix@2284: public static final String JASPER_FILE = "export.waterlevel.pdf.file";
ingo@416:
aheinecke@6601: /** The storage that contains all WQKms objects that are calculated.*/
ingo@389: protected List data;
ingo@389:
aheinecke@6601: /** The storage that contains official fixings if available.*/
aheinecke@6601: protected List officalFixings;
ingo@389:
ingo@389: public void init(Document request, OutputStream out, CallContext context) {
ingo@389: logger.debug("WaterlevelExporter.init");
ingo@389:
ingo@391: super.init(request, out, context);
ingo@389:
ingo@391: this.data = new ArrayList();
ingo@389: }
ingo@389:
ingo@389:
ingo@446: @Override
ingo@446: public void generate()
ingo@446: throws IOException
ingo@446: {
ingo@446: logger.debug("WaterlevelExporter.generate");
ingo@446:
aheinecke@6601: /* Check for official fixings. They should also be included in the
aheinecke@6601: * export but only the calculation result is added with addData */
aheinecke@6601:
aheinecke@6601: officalFixings = new ArrayList();
aheinecke@6601:
aheinecke@6601: for (Artifact art: collection.getArtifactsByName(STATICWQKMSNAME, context)) {
aheinecke@6601: if (art instanceof StaticWQKmsArtifact) {
aheinecke@6601: StaticWQKmsArtifact sart = (StaticWQKmsArtifact) art;
aheinecke@6601: if (!sart.isOfficial()) {
aheinecke@6601: continue;
aheinecke@6601: }
aheinecke@6601:
aheinecke@6601: /* Check that we add the data only once */
aheinecke@6601: WQKms toAdd = sart.getWQKms();
aheinecke@6601: String newName = toAdd.getName();
aheinecke@6601:
aheinecke@6601: boolean exists = false;
aheinecke@6601: for (WQKms wqkm: officalFixings) {
aheinecke@6601: /* The same official fixing could be in two
aheinecke@6601: artifacts/outs so let's deduplicate */
aheinecke@6601: if (wqkm.getName().equals(newName)) {
aheinecke@6601: exists = true;
aheinecke@6601: }
aheinecke@6601: }
aheinecke@6601: if (!exists) {
aheinecke@6601: officalFixings.add(toAdd);
aheinecke@6601: logger.debug("Adding additional offical fixing: " + newName);
aheinecke@6601: }
aheinecke@6601: }
aheinecke@6601: }
aheinecke@6601:
ingo@446: if (facet != null && facet.equals(AbstractExporter.FACET_CSV)) {
ingo@446: generateCSV();
ingo@446: }
ingo@446: else if (facet != null && facet.equals(FACET_WST)) {
ingo@446: generateWST();
ingo@446: }
raimund@2176: else if (facet != null && facet.equals(AbstractExporter.FACET_PDF)) {
raimund@2176: generatePDF();
raimund@2176: }
ingo@446: else {
ingo@446: throw new IOException("invalid facet for exporter");
ingo@446: }
ingo@446: }
ingo@446:
ingo@446:
sascha@701: @Override
sascha@701: protected void addData(Object d) {
sascha@709: if (d instanceof CalculationResult) {
sascha@709: d = ((CalculationResult)d).getData();
sascha@709: if (d instanceof WQKms []) {
sascha@709: data.add((WQKms [])d);
sascha@709: }
felix@4405: else if (d instanceof WQKmsResult) {
felix@4405: data.add(((WQKmsResult) d).getWQKms());
ingo@3462: }
sascha@701: }
ingo@389: }
ingo@389:
ingo@389:
ingo@2035: /**
felix@6576: * Prepare the column titles of waterlevel exports.
ingo@2035: * Titles in this export include the Q value. If a Q value matches a named
ingo@2035: * main value (as HQ100 or MNQ) this named main value should be used as
ingo@2035: * title. This method resets the name of the wqkms object if such
ingo@2035: * named main value fits to the chosen Q.
ingo@2035: *
ingo@2035: * @param winfo A WINFO Artifact.
ingo@2035: * @param wqkms A WQKms object that should be prepared.
ingo@2035: */
ingo@2038: protected String getColumnTitle(WINFOArtifact winfo, WQKms wqkms) {
felix@5131: logger.debug("WaterlevelExporter.getColumnTitle");
ingo@2035:
ingo@2035: String name = wqkms.getName();
ingo@2035:
ingo@2035: logger.debug("Name of WQKms = '" + name + "'");
ingo@2035:
ingo@2038: if (name.indexOf("W=") >= 0) {
ingo@2038: return name;
ingo@2038: }
ingo@2038:
ingo@2035: Matcher m = NUMBERS_PATTERN.matcher(name);
ingo@2035:
ingo@2035: if (m.matches()) {
ingo@2035: String raw = m.group(1);
ingo@2035:
ingo@2035: try {
ingo@2035: double v = Double.valueOf(raw);
ingo@2035:
teichmann@5865: String nmv = RiverUtils.getNamedMainValue(winfo, v);
ingo@2035:
ingo@2035: if (nmv != null && nmv.length() > 0) {
teichmann@5865: nmv = RiverUtils.stripNamedMainValue(nmv);
ingo@2596: nmv += "=" + String.valueOf(v);
ingo@2035: logger.debug("Set named main value '" + nmv + "'");
ingo@2035:
ingo@2038: return nmv;
ingo@2035: }
ingo@2035: }
ingo@2035: catch (NumberFormatException nfe) {
ingo@2035: // do nothing here
ingo@2035: }
ingo@2035: }
ingo@2038:
ingo@2038: return name;
ingo@2035: }
ingo@2035:
ingo@2035:
ingo@2087: protected String getCSVRowTitle(WINFOArtifact winfo, WQKms wqkms) {
ingo@2087: logger.debug("WaterlevelExporter.prepareNamedValue");
ingo@2087:
ingo@2087: String name = wqkms.getName();
ingo@2087:
ingo@2087: logger.debug("Name of WQKms = '" + name + "'");
ingo@2087:
teichmann@5865: WQ_MODE wqmode = RiverUtils.getWQMode(winfo);
ingo@2087:
ingo@2087: if (wqmode == WQ_MODE.WFREE || wqmode == WQ_MODE.QGAUGE) {
ingo@2087: return localizeWQKms(winfo, wqkms);
ingo@2087: }
ingo@2087:
ingo@2087: Double v = wqkms.getRawValue();
ingo@2087:
teichmann@5865: String nmv = RiverUtils.getNamedMainValue(winfo, v);
ingo@2087:
ingo@2087: if (nmv != null && nmv.length() > 0) {
teichmann@5865: nmv = RiverUtils.stripNamedMainValue(nmv);
ingo@2087: logger.debug("Set named main value '" + nmv + "'");
ingo@2087:
ingo@2087: return nmv;
ingo@2087: }
ingo@2087:
ingo@2087: return localizeWQKms(winfo, wqkms);
ingo@2087: }
ingo@2087:
ingo@2087:
felix@3252: /**
felix@3252: * Get a string like 'W=' or 'Q=' with a number following in localized
felix@3252: * format.
felix@3252: */
ingo@2087: protected String localizeWQKms(WINFOArtifact winfo, WQKms wqkms) {
teichmann@5865: WQ_MODE wqmode = RiverUtils.getWQMode(winfo);
ingo@2087: Double rawValue = wqkms.getRawValue();
ingo@2087:
ingo@2087: if (rawValue == null) {
ingo@2087: return wqkms.getName();
ingo@2087: }
ingo@2087:
ingo@2087: NumberFormat nf = Formatter.getRawFormatter(context);
ingo@2087:
ingo@2087: if (wqmode == WQ_MODE.WFREE || wqmode == WQ_MODE.WGAUGE) {
ingo@2087: return "W=" + nf.format(rawValue);
ingo@2087: }
ingo@2087: else {
ingo@2087: return "Q=" + nf.format(rawValue);
ingo@2087: }
ingo@2087: }
ingo@2087:
ingo@2087:
sascha@701: @Override
ingo@391: protected void writeCSVData(CSVWriter writer) {
ingo@391: logger.info("WaterlevelExporter.writeData");
ingo@389:
teichmann@5867: WQ_MODE mode = RiverUtils.getWQMode((D4EArtifact)master);
felix@3252: boolean atGauge = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.WGAUGE;
felix@3252: boolean isQ = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.QFREE;
teichmann@5865: RiverUtils.WQ_INPUT input
teichmann@5867: = RiverUtils.getWQInputMode((D4EArtifact)master);
ingo@2065:
ingo@2045: writeCSVMeta(writer);
ingo@2068: writeCSVHeader(writer, atGauge, isQ);
ingo@416:
aheinecke@6601: Double first = Double.NaN;
aheinecke@6601: Double last = Double.NaN;
aheinecke@6601:
ingo@389: for (WQKms[] tmp: data) {
ingo@389: for (WQKms wqkms: tmp) {
felix@3252: wQKms2CSV(writer, wqkms, atGauge, isQ);
aheinecke@6601: double[] firstLast = wqkms.getFirstLastKM();
aheinecke@6601: if (first.isNaN()) {
aheinecke@6601: /* Initialize */
aheinecke@6601: first = firstLast[0];
aheinecke@6601: last = firstLast[1];
aheinecke@6601: }
aheinecke@6601: if (firstLast[0] > firstLast[1]) {
aheinecke@6601: /* Calculating upstream we assert that it is
aheinecke@6601: * impossible that the direction changes during this
aheinecke@6601: * loop */
aheinecke@6601: first = Math.max(first, firstLast[0]);
aheinecke@6601: last = Math.min(last, firstLast[1]);
aheinecke@6601: } else if (firstLast[0] < firstLast[1]) {
aheinecke@6601: first = Math.min(first, firstLast[0]);
aheinecke@6601: last = Math.max(last, firstLast[1]);
aheinecke@6601: } else {
aheinecke@6601: first = last = firstLast[0];
aheinecke@6601: }
ingo@389: }
ingo@389: }
aheinecke@6601: /* Append the official fixing at the bottom */
aheinecke@6601: for (WQKms wqkms: officalFixings) {
aheinecke@6603: wQKms2CSV(writer, filterWQKms(wqkms, first, last), atGauge, isQ);
aheinecke@6603: }
aheinecke@6603: }
aheinecke@6601:
aheinecke@6603:
aheinecke@6603: /** Filter a wqkms object to a distance.
aheinecke@6603: *
aheinecke@6603: * To handle upstream / downstream and to limit
aheinecke@6603: * the officialFixings to the calculation distance
aheinecke@6603: * we create a new wqkms object here and fill it only
aheinecke@6603: * with the relevant data.
aheinecke@6603: *
aheinecke@6603: * @param wqkms: The WQKms Object to filter
aheinecke@6603: * @param first: The fist kilometer of the range
aheinecke@6603: * @param last: The last kilometer of the range
aheinecke@6603: *
aheinecke@6603: * @return A new WQKms with the relevant data sorted by direction
aheinecke@6603: */
aheinecke@6603: private WQKms filterWQKms(WQKms wqkms, Double first, Double last) {
aheinecke@6603: if (first.isNaN() || last.isNaN()) {
aheinecke@6603: logger.warn("Filtering official fixing without valid first/last.");
aheinecke@6603: return wqkms;
aheinecke@6603: }
aheinecke@6603: int firstIdx = first > last ? wqkms.size() - 1 : 0;
aheinecke@6603: int lastIdx = first > last ? 0 : wqkms.size() -1;
aheinecke@6603: WQKms filtered = new WQKms (wqkms.size());
aheinecke@6603: filtered.setReferenceSystem(wqkms.getReferenceSystem());
aheinecke@6603: filtered.setName(wqkms.getName());
aheinecke@6603: double [] dp = new double [3];
aheinecke@6603:
aheinecke@6603: if (first > last) {
aheinecke@6603: for (int i = wqkms.size() - 1; i >= 0; i--) {
aheinecke@6603: dp = wqkms.get(i, dp);
aheinecke@6605: if (dp[2] <= first + 1E-5 && dp[2] > last - 1E-5) {
aheinecke@6603: filtered.add(dp[0], dp[1], dp[2]);
aheinecke@6601: }
aheinecke@6601: }
aheinecke@6603: } else {
aheinecke@6603: for (int i = 0; i < wqkms.size(); i++) {
aheinecke@6603: dp = wqkms.get(i, dp);
aheinecke@6605: if (dp[2] < last + 1E-5 && dp[2] > first - 1E-5) {
aheinecke@6603: filtered.add(dp[0], dp[1], dp[2]);
aheinecke@6603: }
aheinecke@6603: }
aheinecke@6601: }
aheinecke@6603: return filtered;
ingo@389: }
aheinecke@6603:
ingo@389:
ingo@2045: protected void writeCSVMeta(CSVWriter writer) {
ingo@2045: logger.info("WaterlevelExporter.writeCSVMeta");
ingo@2045:
teichmann@5865: // TODO use Access instead of RiverUtils
felix@4859:
ingo@2045: CallMeta meta = context.getMeta();
ingo@2045:
teichmann@5867: D4EArtifact flys = (D4EArtifact) master;
ingo@2045:
ingo@2045: writer.writeNext(new String[] {
ingo@2045: Resources.getMsg(
ingo@2045: meta,
ingo@2045: CSV_META_RESULT,
ingo@2045: CSV_META_RESULT,
teichmann@5865: new Object[] { RiverUtils.getRivername(flys) })
ingo@2045: });
ingo@2045:
ingo@2045: Locale locale = Resources.getLocale(meta);
ingo@2045: DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
ingo@2045:
ingo@2045: writer.writeNext(new String[] {
ingo@2045: Resources.getMsg(
ingo@2045: meta,
ingo@2045: CSV_META_CREATION,
ingo@2045: CSV_META_CREATION,
ingo@2045: new Object[] { df.format(new Date()) })
ingo@2045: });
ingo@2045:
ingo@2045: writer.writeNext(new String[] {
ingo@2045: Resources.getMsg(
ingo@2045: meta,
ingo@2045: CSV_META_CALCULATIONBASE,
ingo@2045: CSV_META_CALCULATIONBASE,
ingo@2045: new Object[] { "" }) // TODO what is required at this place?
ingo@2045: });
ingo@2045:
ingo@2045: writer.writeNext(new String[] {
ingo@2045: Resources.getMsg(
ingo@2045: meta,
ingo@2045: CSV_META_RIVER,
ingo@2045: CSV_META_RIVER,
teichmann@5865: new Object[] { RiverUtils.getRivername(flys) })
ingo@2045: });
ingo@2045:
teichmann@6101: RangeAccess rangeAccess = new RangeAccess(flys);
felix@4859: double[] kms = rangeAccess.getKmRange();
ingo@2045: writer.writeNext(new String[] {
ingo@2045: Resources.getMsg(
ingo@2045: meta,
ingo@2045: CSV_META_RANGE,
ingo@2045: CSV_META_RANGE,
ingo@2045: new Object[] { kms[0], kms[kms.length-1] })
ingo@2045: });
ingo@2045:
ingo@2045: writer.writeNext(new String[] {
ingo@2045: Resources.getMsg(
ingo@2045: meta,
ingo@2045: CSV_META_GAUGE,
ingo@2045: CSV_META_GAUGE,
teichmann@5865: new Object[] { RiverUtils.getGaugename(flys) })
ingo@2045: });
ingo@2045:
teichmann@5865: RiverUtils.WQ_MODE wq = RiverUtils.getWQMode(flys);
teichmann@5865: if (wq == RiverUtils.WQ_MODE.QFREE || wq == RiverUtils.WQ_MODE.QGAUGE) {
teichmann@5865: double[] qs = RiverUtils.getQs(flys);
teichmann@5865: RiverUtils.WQ_INPUT input = RiverUtils.getWQInputMode(flys);
ingo@2045:
ingo@2423: String data = "";
ingo@2045:
teichmann@5865: if ((input == RiverUtils.WQ_INPUT.ADAPTED ||
teichmann@5865: input == RiverUtils.WQ_INPUT.RANGE) &&
ingo@2423: qs != null && qs.length > 0)
ingo@2423: {
ingo@2423: data = String.valueOf(qs[0]);
ingo@2423: data += " - " + String.valueOf(qs[qs.length-1]);
ingo@2423: }
teichmann@5865: else if (input == RiverUtils.WQ_INPUT.SINGLE && qs != null){
ingo@2423: data = String.valueOf(qs[0]);
ingo@2423: for (int i = 1; i < qs.length; i++) {
ingo@2423: data += ", " + String.valueOf(qs[i]);
ingo@2423: }
ingo@2045: }
ingo@2045: else {
ingo@2045: logger.warn("Could not determine Q range!");
ingo@2045: }
ingo@2045:
ingo@2045: writer.writeNext(new String[] {
ingo@2045: Resources.getMsg(
ingo@2045: meta,
ingo@2045: CSV_META_Q,
ingo@2045: CSV_META_Q,
ingo@2423: new Object[] {data})
ingo@2045: });
ingo@2045: }
ingo@2045: else {
teichmann@5865: double[] ws = RiverUtils.getWs(flys);
ingo@2045:
ingo@2045: String lower = "";
ingo@2045: String upper = "";
ingo@2045:
ingo@2045: if (ws != null && ws.length > 0) {
ingo@2045: lower = String.valueOf(ws[0]);
ingo@2045: upper = String.valueOf(ws[ws.length-1]);
ingo@2045: }
ingo@2045: else {
ingo@2045: logger.warn("Could not determine W range!");
ingo@2045: }
ingo@2045:
ingo@2045: writer.writeNext(new String[] {
ingo@2045: Resources.getMsg(
ingo@2045: meta,
ingo@2045: CSV_META_W,
ingo@2045: CSV_META_W,
ingo@2045: new Object[] { lower, upper })
ingo@2045: });
ingo@2045: }
ingo@2045:
ingo@2045: writer.writeNext(new String[] { "" });
ingo@2045: }
ingo@2045:
ingo@2045:
felix@4979: /**
felix@4979: * Write the header, with different headings depending on whether at a
felix@4979: * gauge or at a location.
felix@4979: */
ingo@2068: protected void writeCSVHeader(
ingo@2068: CSVWriter writer,
ingo@2068: boolean atGauge,
ingo@2068: boolean isQ
ingo@2068: ) {
ingo@416: logger.info("WaterlevelExporter.writeCSVHeader");
ingo@416:
teichmann@5867: String unit = RiverUtils.getRiver((D4EArtifact) master).getWstUnit().getName();
felix@5133:
ingo@2065: if (atGauge) {
ingo@2065: writer.writeNext(new String[] {
ingo@2065: msg(CSV_KM_HEADER, DEFAULT_CSV_KM_HEADER),
felix@5133: msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER, new Object[] { unit }),
ingo@2065: msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER),
ingo@2068: (isQ
ingo@2068: ? msg(CSV_Q_DESC_HEADER, DEFAULT_CSV_Q_DESC_HEADER)
ingo@2068: : msg(CSV_W_DESC_HEADER, DEFAULT_CSV_W_DESC_HEADER)),
ingo@2065: msg(CSV_LOCATION_HEADER, DEFAULT_CSV_LOCATION_HEADER),
ingo@2065: msg(CSV_GAUGE_HEADER, DEFAULT_CSV_GAUGE_HEADER)
ingo@2065: });
ingo@2065: }
ingo@2065: else {
ingo@2065: writer.writeNext(new String[] {
ingo@2065: msg(CSV_KM_HEADER, DEFAULT_CSV_KM_HEADER),
felix@5133: // TODO flys/issue1128 (unit per river)
felix@5133: msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER, new Object[] { unit }),
ingo@2065: msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER),
ingo@2065: msg(CSV_LOCATION_HEADER, DEFAULT_CSV_LOCATION_HEADER)
ingo@2065: });
ingo@2065: }
ingo@416: }
ingo@416:
ingo@416:
felix@5112: /** Linearly search for gauge which is valid at km. */
teichmann@5587: private static Gauge findGauge(double km, List gauges) {
felix@5112: for (Gauge gauge: gauges) {
teichmann@5587: if (gauge.getRange().contains(km)) {
felix@5112: return gauge;
felix@5112: }
felix@5112: }
felix@5112: return null;
felix@5112: }
felix@5112:
teichmann@5587: private static Segment findSegment(double km, List segments) {
teichmann@5587: for (Segment segment: segments) {
teichmann@5587: if (segment.inside(km)) {
teichmann@5587: return segment;
teichmann@5587: }
teichmann@5587: }
teichmann@5587: return null;
teichmann@5587: }
teichmann@5587:
felix@5112:
teichmann@5867: private void writeRow4(CSVWriter writer, double wqkm[], D4EArtifact flys) {
felix@5112: NumberFormat kmf = getKmFormatter();
felix@5112: NumberFormat wf = getWFormatter();
felix@5112: NumberFormat qf = getQFormatter();
aheinecke@6607: writer.writeNext(new String[] {
aheinecke@6607: kmf.format(wqkm[2]),
aheinecke@6607: wf.format(wqkm[0]),
aheinecke@6607: qf.format(wqkm[1]),
aheinecke@6607: RiverUtils.getLocationDescription(flys, wqkm[2])
aheinecke@6607: });
felix@5112: }
felix@5112:
felix@5112:
felix@5112: /** Write an csv-row at gauge location. */
felix@5112: private void writeRow6(CSVWriter writer, double wqkm[], String wOrQDesc,
teichmann@5867: D4EArtifact flys, String gaugeName) {
felix@5112: NumberFormat kmf = getKmFormatter();
felix@5112: NumberFormat wf = getWFormatter();
felix@5112: NumberFormat qf = getQFormatter();
felix@5112:
felix@5112: writer.writeNext(new String[] {
felix@5112: kmf.format(wqkm[2]),
felix@5112: wf.format(wqkm[0]),
felix@5112: qf.format(wqkm[1]),
felix@5112: wOrQDesc,
teichmann@5865: RiverUtils.getLocationDescription(flys, wqkm[2]),
felix@5112: gaugeName
felix@5112: });
felix@5112: }
felix@5112:
aheinecke@6606: private String getDesc(WQKms wqkms, boolean isQ)
aheinecke@6606: {
aheinecke@6606: D4EArtifact flys = (D4EArtifact) master;
aheinecke@6606: String colDesc = "";
aheinecke@6606:
aheinecke@6606: if (flys instanceof WINFOArtifact && isQ) {
aheinecke@6606: colDesc = getCSVRowTitle((WINFOArtifact)flys, wqkms);
aheinecke@6606: }
aheinecke@6606: else if (!isQ) {
aheinecke@6606: Double value = RiverUtils.getValueFromWQ(wqkms);
aheinecke@6606: colDesc = (value != null) ?
aheinecke@6606: Formatter.getWaterlevelW(context).format(value) : null;
aheinecke@6606: }
aheinecke@6606:
aheinecke@6606: if (flys instanceof WINFOArtifact) {
aheinecke@6606: if (wqkms != null && wqkms.getRawValue() != null) {
aheinecke@6606: WINFOArtifact winfo = (WINFOArtifact) flys;
aheinecke@6606: colDesc = RiverUtils.getNamedMainValue(winfo, wqkms.getRawValue());
aheinecke@6606: // For 'W am Pegel' s
aheinecke@6606: if (colDesc == null) {
aheinecke@6606: colDesc = ((D4EArtifact)master).getDataAsString("wq_single");
aheinecke@6606: }
aheinecke@6606: }
aheinecke@6606: }
aheinecke@6606: return colDesc == null ? "" : colDesc;
aheinecke@6606: }
felix@5112:
felix@3252: /**
felix@3252: * Write "rows" of csv data from wqkms with writer.
felix@3252: */
ingo@2068: protected void wQKms2CSV(
ingo@2068: CSVWriter writer,
ingo@2068: WQKms wqkms,
ingo@2068: boolean atGauge,
felix@3252: boolean isQ
ingo@2068: ) {
ingo@389: logger.debug("WaterlevelExporter.wQKms2CSV");
ingo@389:
teichmann@4836: // Skip constant data.
teichmann@4836: if (wqkms instanceof ConstantWQKms) {
teichmann@4836: return;
teichmann@4836: }
teichmann@4836:
ingo@418: NumberFormat kmf = getKmFormatter();
ingo@418: NumberFormat wf = getWFormatter();
ingo@418: NumberFormat qf = getQFormatter();
ingo@418:
ingo@389: int size = wqkms.size();
ingo@389: double[] result = new double[3];
ingo@389:
teichmann@5867: D4EArtifact flys = (D4EArtifact) master;
teichmann@5865: List gauges = RiverUtils.getGauges(flys);
teichmann@5865: Gauge gauge = RiverUtils.getGauge(flys);
ingo@2066: String gaugeName = gauge.getName();
ingo@2066: String desc = "";
ingo@2066: String notinrange = msg(
ingo@2066: CSV_NOT_IN_GAUGE_RANGE,
ingo@2066: DEFAULT_CSV_NOT_IN_GAUGE_RANGE);
aheinecke@6606: List segments = null;
aheinecke@6606: boolean isFixRealize = false;
ingo@2066:
ingo@2066: double a = gauge.getRange().getA().doubleValue();
ingo@2066: double b = gauge.getRange().getB().doubleValue();
sascha@2144: long startTime = System.currentTimeMillis();
sascha@2144:
aheinecke@6606: desc = getDesc(wqkms, isQ);
aheinecke@6606:
aheinecke@6606: if (flys instanceof FixationArtifact) {
felix@5103: // Get W/Q input per gauge for this case.
teichmann@6101: FixRealizingAccess fixAccess = new FixRealizingAccess(flys);
felix@5103: segments = fixAccess.getSegments();
teichmann@5587: if (segments != null && !segments.isEmpty()) {
felix@5112: isFixRealize = true;
felix@5112: }
felix@5103: }
sascha@2612:
teichmann@5587: if (atGauge) { // "At gauge" needs more output.
ingo@389:
teichmann@5587: // Kms tend to be close together so caching the last sector
teichmann@5587: // is a good time saving heuristic.
teichmann@5587: Segment lastSegment = null;
teichmann@5587: Gauge lastGauge = null;
teichmann@5587:
teichmann@5587: NumberFormat nf =
teichmann@5587: Formatter.getFormatter(context.getMeta(), 0, 0);
teichmann@5587:
teichmann@5587: for (int i = 0; i < size; ++i) {
teichmann@5587: result = wqkms.get(i, result);
teichmann@5587: double km = result[2];
teichmann@5587:
teichmann@5587: if (segments != null) {
teichmann@5587: Segment found = lastSegment != null
teichmann@5587: && lastSegment.inside(km)
teichmann@5587: ? lastSegment
teichmann@5587: : findSegment(km, segments);
teichmann@5587:
teichmann@5587: if (found != null) {
aheinecke@6606: desc = nf.format(found.getValues()[0]);
felix@5103: }
teichmann@5587: lastSegment = found;
felix@5103: }
felix@5103:
felix@5112: String gaugeN;
felix@5112: if (isFixRealize) {
teichmann@5587: Gauge found = lastGauge != null
teichmann@5587: && lastGauge.getRange().contains(km)
teichmann@5587: ? lastGauge
teichmann@5587: : findGauge(km, gauges);
teichmann@5587:
teichmann@5587: gaugeN = found != null ? found.getName() : notinrange;
teichmann@5587: lastGauge = found;
felix@5112: }
felix@5112: else {
felix@4979: // TODO issue1114: Take correct gauge
teichmann@5587: gaugeN = km >= a && km <= b
ingo@2066: ? gaugeName
felix@5112: : notinrange;
felix@5112: }
aheinecke@6606: writeRow6(writer, result, desc, flys, gaugeN);
ingo@2065: }
teichmann@5587: }
teichmann@5587: else { // Not at gauge.
teichmann@5587: for (int i = 0; i < size; ++i) {
teichmann@5587: result = wqkms.get(i, result);
felix@5112: writeRow4(writer, result, flys);
ingo@2065: }
ingo@389: }
sascha@2144:
sascha@2144: long stopTime = System.currentTimeMillis();
sascha@2144:
sascha@2144: if (logger.isDebugEnabled()) {
sascha@2144: logger.debug("Writing CSV took " +
sascha@2144: (float)(stopTime-startTime)/1000f + " secs.");
sascha@2144: }
ingo@389: }
ingo@418:
ingo@418:
ingo@418: /**
ingo@446: * Generates the output in WST format.
ingo@446: */
ingo@446: protected void generateWST()
ingo@446: throws IOException
ingo@446: {
ingo@446: logger.info("WaterlevelExporter.generateWST");
ingo@446:
ingo@446: int cols = data.get(0).length;
ingo@446: WstWriter writer = new WstWriter(cols);
ingo@446:
ingo@446: writeWSTData(writer);
ingo@446:
ingo@446: writer.write(out);
ingo@446: }
ingo@446:
ingo@446:
ingo@446: protected void writeWSTData(WstWriter writer) {
ingo@446: logger.debug("WaterlevelExporter.writeWSTData");
ingo@446:
ingo@749: double[] result = new double[4];
ingo@446:
ingo@446: for (WQKms[] tmp: data) {
ingo@446: for (WQKms wqkms: tmp) {
rrenkert@5105: if (wqkms instanceof ConstantWQKms) {
rrenkert@5105: continue;
rrenkert@5105: }
ingo@446: int size = wqkms != null ? wqkms.size() : 0;
ingo@446:
ingo@450: addWSTColumn(writer, wqkms);
ingo@447:
ingo@446: for (int i = 0; i < size; i++) {
ingo@446: result = wqkms.get(i, result);
ingo@446:
ingo@446: writer.add(result);
ingo@446: }
ingo@749:
ingo@749: if (wqkms instanceof WQCKms) {
ingo@749: addWSTColumn(writer, wqkms);
ingo@749:
ingo@749: for (int c = 0; c < size; c++) {
ingo@749: result = wqkms.get(c, result);
ingo@749:
ingo@749: writer.addCorrected(result);
ingo@749: }
ingo@749: }
ingo@446: }
ingo@446: }
ingo@446: }
ingo@446:
ingo@446:
ingo@2038: /**
felix@6576: * Register a new column at writer. The name /
ingo@2038: * title of the column depends on the Q or W value of wqkms. If a Q
ingo@2038: * was selected and the Q fits to a named main value, the title is set to
ingo@2038: * the named main value. Otherwise, the name returned by
ingo@2038: * WQKms.getName() is set.
ingo@2038: *
ingo@2038: * @param writer The WstWriter.
ingo@2038: * @param wqkms The new WST column.
ingo@2038: */
ingo@450: protected void addWSTColumn(WstWriter writer, WQKms wqkms) {
teichmann@4836: if (wqkms instanceof ConstantWQKms) {
teichmann@4836: return;
teichmann@4836: }
ingo@2038: if (master instanceof WINFOArtifact) {
ingo@2038: writer.addColumn(getColumnTitle((WINFOArtifact) master, wqkms));
ingo@2038: }
ingo@2038: else {
ingo@2038: writer.addColumn(wqkms.getName());
ingo@2038: }
ingo@450: }
ingo@450:
ingo@450:
raimund@2176: @Override
raimund@2176: protected void writePDF(OutputStream out) {
raimund@2176: logger.debug("write PDF");
raimund@2176: WKmsJRDataSource source = createJRData();
raimund@2185:
raimund@2185: String jasperFile = Resources.getMsg(
raimund@2185: context.getMeta(),
raimund@2185: JASPER_FILE,
raimund@2185: "/jasper/waterlevel_en.jasper");
raimund@2185: String confPath = Config.getConfigDirectory().toString();
raimund@2185:
raimund@2185:
raimund@2176: Map parameters = new HashMap();
raimund@2176: parameters.put("ReportTitle", "Exported Data");
raimund@2176: try {
raimund@2176: JasperPrint print = JasperFillManager.fillReport(
raimund@2185: confPath + jasperFile,
raimund@2176: parameters,
raimund@2176: source);
raimund@2176: JasperExportManager.exportReportToPdfStream(print, out);
raimund@2176: }
raimund@2176: catch(JRException je) {
teichmann@4836: logger.warn("Error generating PDF Report!", je);
raimund@2176: }
raimund@2176: }
raimund@2176:
raimund@2176: protected WKmsJRDataSource createJRData() {
raimund@2176: WKmsJRDataSource source = new WKmsJRDataSource();
raimund@2176:
teichmann@5867: WQ_MODE mode = RiverUtils.getWQMode((D4EArtifact)master);
raimund@2176: boolean atGauge = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.WGAUGE;
raimund@2176: boolean isQ = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.QFREE;
raimund@2176:
raimund@2176: addMetaData(source);
raimund@2176: for (WQKms[] tmp: data) {
raimund@2176: for (WQKms wqkms: tmp) {
raimund@2176: addWKmsData(wqkms, atGauge, isQ, source);
raimund@2176: }
raimund@2176: }
raimund@2176: return source;
raimund@2176: }
raimund@2176:
raimund@2176: protected void addMetaData(WKmsJRDataSource source) {
raimund@2176: CallMeta meta = context.getMeta();
raimund@2176:
teichmann@5867: D4EArtifact flys = (D4EArtifact) master;
raimund@2176:
teichmann@5865: source.addMetaData ("river", RiverUtils.getRivername(flys));
raimund@2176:
raimund@2176: Locale locale = Resources.getLocale(meta);
raimund@2176: DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
raimund@2176:
raimund@2176: source.addMetaData("date", df.format(new Date()));
raimund@2176:
teichmann@6101: RangeAccess rangeAccess = new RangeAccess(flys);
felix@4859: double[] kms = rangeAccess.getKmRange();
raimund@2176: source.addMetaData("range", kms[0] + " - " + kms[kms.length-1]);
raimund@2176:
teichmann@5865: source.addMetaData("gauge", RiverUtils.getGaugename(flys));
raimund@2176:
raimund@2176: source.addMetaData("calculation", Resources.getMsg(
raimund@2176: locale,
raimund@2176: PDF_HEADER_MODE,
raimund@2176: "Waterlevel"));
raimund@2176: }
raimund@2176:
raimund@2176: protected void addWKmsData(
raimund@2176: WQKms wqkms,
raimund@2176: boolean atGauge,
raimund@2176: boolean isQ,
raimund@2176: WKmsJRDataSource source)
raimund@2176: {
raimund@2176: logger.debug("WaterlevelExporter.addWKmsData");
raimund@2176:
teichmann@4836: // Skip constant data.
teichmann@4836: if (wqkms instanceof ConstantWQKms) {
teichmann@4836: return;
teichmann@4836: }
teichmann@4836:
raimund@2176: NumberFormat kmf = getKmFormatter();
raimund@2176: NumberFormat wf = getWFormatter();
raimund@2176: NumberFormat qf = getQFormatter();
raimund@2176:
raimund@2176: int size = wqkms.size();
raimund@2176: double[] result = new double[3];
raimund@2176:
teichmann@5867: D4EArtifact flys = (D4EArtifact) master;
teichmann@5865: Gauge gauge = RiverUtils.getGauge(flys);
raimund@2176: String gaugeName = gauge.getName();
raimund@2176: String desc = "";
raimund@2176: String notinrange = msg(
raimund@2176: CSV_NOT_IN_GAUGE_RANGE,
raimund@2176: DEFAULT_CSV_NOT_IN_GAUGE_RANGE);
raimund@2176:
raimund@2176: double a = gauge.getRange().getA().doubleValue();
raimund@2176: double b = gauge.getRange().getB().doubleValue();
raimund@2176:
aheinecke@6606: desc = getDesc(wqkms, isQ);
raimund@2176: long startTime = System.currentTimeMillis();
raimund@2176:
raimund@2176: for (int i = 0; i < size; i ++) {
raimund@2176: result = wqkms.get(i, result);
raimund@2176:
raimund@2176: if (atGauge) {
raimund@2176: source.addData(new String[] {
raimund@2176: kmf.format(result[2]),
raimund@2176: wf.format(result[0]),
raimund@2176: qf.format(result[1]),
raimund@2176: desc,
teichmann@5865: RiverUtils.getLocationDescription(flys, result[2]),
raimund@2176: result[2] >= a && result[2] <= b
raimund@2176: ? gaugeName
raimund@2176: : notinrange
raimund@2176: });
raimund@2176: }
raimund@2176: else {
raimund@2176: source.addData(new String[] {
raimund@2176: kmf.format(result[2]),
raimund@2176: wf.format(result[0]),
raimund@2176: qf.format(result[1]),
raimund@2764: desc,
teichmann@5865: RiverUtils.getLocationDescription(flys, result[2]),
raimund@2764: result[2] >= a && result[2] <= b
raimund@2764: ? gaugeName
raimund@2764: : notinrange
raimund@2176: });
raimund@2176: }
raimund@2176: }
raimund@2176:
raimund@2176: long stopTime = System.currentTimeMillis();
raimund@2176:
raimund@2176: if (logger.isDebugEnabled()) {
raimund@2176: logger.debug("Writing PDF data took " +
raimund@2176: (float)(stopTime-startTime)/1000f + " secs.");
raimund@2176: }
raimund@2176: }
ingo@389: }
ingo@389: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :