gernotbelger@9457: /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde gernotbelger@9457: * Software engineering by Intevation GmbH gernotbelger@9457: * gernotbelger@9457: * This file is Free Software under the GNU AGPL (>=v3) gernotbelger@9457: * and comes with ABSOLUTELY NO WARRANTY! Check out the gernotbelger@9457: * documentation coming with Dive4Elements River for details. gernotbelger@9457: */ gernotbelger@9457: gernotbelger@9457: package org.dive4elements.river.exports; gernotbelger@9457: gernotbelger@9457: import java.io.OutputStream; gernotbelger@9457: import java.text.NumberFormat; gernotbelger@9457: import java.util.ArrayList; gernotbelger@9457: import java.util.HashMap; gernotbelger@9457: import java.util.List; gernotbelger@9457: import java.util.Map; gernotbelger@9457: gernotbelger@9457: import org.apache.log4j.Logger; gernotbelger@9457: import org.dive4elements.river.artifacts.D4EArtifact; gernotbelger@9457: import org.dive4elements.river.artifacts.access.RangeAccess; gernotbelger@9457: import org.dive4elements.river.artifacts.common.DefaultCalculationResults; gernotbelger@9457: import org.dive4elements.river.artifacts.common.ExportContextPDF; gernotbelger@9457: import org.dive4elements.river.artifacts.common.GeneralResultType; gernotbelger@9457: import org.dive4elements.river.artifacts.common.JasperReporter; gernotbelger@9457: import org.dive4elements.river.artifacts.common.MetaAndTableJRDataSource; gernotbelger@9457: import org.dive4elements.river.artifacts.model.ConstantWQKms; gernotbelger@9457: import org.dive4elements.river.artifacts.model.DischargeTables; gernotbelger@9457: import org.dive4elements.river.artifacts.model.WQKms; gernotbelger@9457: import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils; gernotbelger@9457: import org.dive4elements.river.artifacts.sinfo.util.RiverInfo; gernotbelger@9457: import org.dive4elements.river.model.Gauge; gernotbelger@9457: import org.dive4elements.river.utils.Formatter; gernotbelger@9457: import org.dive4elements.river.utils.RiverUtils; gernotbelger@9457: import org.dive4elements.river.utils.RiverUtils.WQ_MODE; gernotbelger@9457: gernotbelger@9457: import au.com.bytecode.opencsv.CSVWriter; gernotbelger@9457: import net.sf.jasperreports.engine.JRException; gernotbelger@9457: gernotbelger@9457: /** gernotbelger@9457: * Generates different output formats (wst, csv, pdf) of data that resulted from gernotbelger@9457: * a waterlevel computation. gernotbelger@9457: * gernotbelger@9457: * @author Ingo Weinzierl gernotbelger@9457: */ gernotbelger@9457: public class FixWaterlevelExporter extends WaterlevelExporter { gernotbelger@9457: gernotbelger@9457: /** The log used in this exporter. */ gernotbelger@9457: private static Logger log = Logger.getLogger(FixWaterlevelExporter.class); gernotbelger@9457: gernotbelger@9457: private final Map gaugeQ_W_Map = new HashMap<>(); gernotbelger@9457: gernotbelger@9457: @Override gernotbelger@9457: protected void writeRow4(final CSVWriter writer, final double wqkm[], final D4EArtifact flys, final Gauge gauge) { gernotbelger@9457: final NumberFormat kmf = getKmFormatter(); gernotbelger@9457: final NumberFormat wf = getWFormatter(); gernotbelger@9457: final NumberFormat qf = getQFormatter(); gernotbelger@9457: gernotbelger@9457: final String waterlevel = getWaterlevel(wqkm[1], gauge); gernotbelger@9457: gernotbelger@9457: writer.writeNext(new String[] { kmf.format(wqkm[2]), wf.format(wqkm[0]), waterlevel, qf.format(RiverUtils.roundQ(wqkm[1])), gernotbelger@9457: RiverUtils.getLocationDescription(flys, wqkm[2]) }); gernotbelger@9457: } gernotbelger@9457: gernotbelger@9457: /** Write an csv-row at gauge location. */ gernotbelger@9457: @Override gernotbelger@9457: protected void writeRow6(final CSVWriter writer, final double wqkm[], final String wOrQDesc, final D4EArtifact flys, final Gauge gauge) { gernotbelger@9457: final NumberFormat kmf = getKmFormatter(); gernotbelger@9457: final NumberFormat wf = getWFormatter(); gernotbelger@9457: final NumberFormat qf = getQFormatter(); gernotbelger@9457: gernotbelger@9457: final String waterlevel = getWaterlevel(wqkm[1], gauge); gernotbelger@9457: gernotbelger@9457: writer.writeNext(new String[] { kmf.format(wqkm[2]), wf.format(wqkm[0]), waterlevel, qf.format(RiverUtils.roundQ(wqkm[1])), wOrQDesc, gernotbelger@9457: RiverUtils.getLocationDescription(flys, wqkm[2]), gauge.getName() }); gernotbelger@9457: } gernotbelger@9457: gernotbelger@9457: private String getWaterlevel(final double discharge, final Gauge gauge) { gernotbelger@9457: final NumberFormat formatter = Formatter.getWaterlevelW(this.context); gernotbelger@9457: final Double waterlevel = this.getWforGaugeAndQ(gauge, discharge); gernotbelger@9457: return formatter.format(waterlevel); gernotbelger@9457: } gernotbelger@9457: gernotbelger@9457: private Double getWforGaugeAndQ(final Gauge gauge, final double q) { gernotbelger@9457: gernotbelger@9457: final String key = gauge.getName() + String.valueOf(q); gernotbelger@9457: if (!this.gaugeQ_W_Map.containsKey(key)) { gernotbelger@9457: gernotbelger@9457: final DischargeTables dct = new DischargeTables(gauge.getRiver().getName(), gauge.getName()); gernotbelger@9457: final double[] qs = DischargeTables.getWsForQ(dct.getFirstTable(), q); // TODO: KLÄREN, welche Abflusstabelle genommen werden soll! gernotbelger@9457: if (qs != null && qs.length > 0) { gernotbelger@9457: this.gaugeQ_W_Map.put(key, qs[0]); gernotbelger@9457: } gernotbelger@9457: } gernotbelger@9457: return this.gaugeQ_W_Map.get(key); gernotbelger@9457: } gernotbelger@9457: gernotbelger@9457: /** gernotbelger@9457: * Write the header, with different headings depending on whether at a gernotbelger@9457: * gauge or at a location. gernotbelger@9457: */ gernotbelger@9457: gernotbelger@9457: @Override gernotbelger@9457: protected void writeCSVHeader(final CSVWriter writer, final boolean atGauge, final boolean isQ) { gernotbelger@9457: log.info("WaterlevelExporter.writeCSVHeader"); gernotbelger@9457: gernotbelger@9457: final String unit = RiverUtils.getRiver((D4EArtifact) this.master).getWstUnit().getName(); gernotbelger@9457: gernotbelger@9457: final String headerWamPegelNeu = msg("fix.export.csv.w_at_gauge"); gernotbelger@9457: gernotbelger@9457: if (atGauge) { gernotbelger@9457: writer.writeNext(new String[] { msg(CSV_KM_HEADER, DEFAULT_CSV_KM_HEADER), msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER, new Object[] { unit }), gernotbelger@9457: headerWamPegelNeu, msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER), gernotbelger@9457: gernotbelger@9457: // FIXME: use WaterlevelDescriptionBuilder instead and also remove all this duplicate code. gernotbelger@9457: (isQ ? msg(CSV_Q_DESC_HEADER, DEFAULT_CSV_Q_DESC_HEADER) : msg(CSV_W_DESC_HEADER, DEFAULT_CSV_W_DESC_HEADER)), gernotbelger@9457: msg(CSV_LOCATION_HEADER, DEFAULT_CSV_LOCATION_HEADER), msg(CSV_GAUGE_HEADER, DEFAULT_CSV_GAUGE_HEADER) }); gernotbelger@9457: } else { gernotbelger@9457: writer.writeNext(new String[] { msg(CSV_KM_HEADER, DEFAULT_CSV_KM_HEADER), msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER, new Object[] { unit }), gernotbelger@9457: headerWamPegelNeu, msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER), msg(CSV_LOCATION_HEADER, DEFAULT_CSV_LOCATION_HEADER) }); gernotbelger@9457: } gernotbelger@9457: } gernotbelger@9457: gernotbelger@9457: @Override gernotbelger@9457: protected void writePDF(final OutputStream out) { gernotbelger@9457: gernotbelger@9457: log.debug("write PDF"); gernotbelger@9457: gernotbelger@9457: final MetaAndTableJRDataSource source = new MetaAndTableJRDataSource(); gernotbelger@9457: final String jasperFile = "/jasper/templates/fix_waterlevel.jrxml"; // "/jasper/fix_waterlevel_en.jasper"); gernotbelger@9457: gernotbelger@9457: addMetaData(source); gernotbelger@9457: try { gernotbelger@9457: final List sorted = getRows(); // Custom Result could be nice, too... gernotbelger@9457: for (final String[] list : sorted) { gernotbelger@9457: source.addData(list); gernotbelger@9457: } gernotbelger@9457: gernotbelger@9457: final JasperReporter reporter = new JasperReporter(); gernotbelger@9457: reporter.addReport(jasperFile, source); gernotbelger@9457: reporter.exportPDF(out); gernotbelger@9457: } gernotbelger@9457: catch (final JRException je) { gernotbelger@9457: log.warn("Error generating PDF Report!", je); gernotbelger@9457: } gernotbelger@9457: } gernotbelger@9457: gernotbelger@9457: private void addMetaData(final MetaAndTableJRDataSource source) { gernotbelger@9457: final D4EArtifact flys = (D4EArtifact) this.master; gernotbelger@9457: final String user = CalculationUtils.findArtifactUser(this.context, flys); gernotbelger@9457: final RangeAccess ra = new RangeAccess(flys); gernotbelger@9457: final RiverInfo ri = new RiverInfo(ra.getRiver()); gernotbelger@9457: gernotbelger@9457: final DefaultCalculationResults results = new DefaultCalculationResults(msg("calculation.analysis"), user, ri, ra.getRange()); gernotbelger@9457: final ExportContextPDF contextPdf = new ExportContextPDF(this.context, results); gernotbelger@9457: contextPdf.addJRMetaDataDefaults(source); gernotbelger@9457: contextPdf.addJRMetaDataForModules(source); gernotbelger@9457: gernotbelger@9457: /* column headings */ gernotbelger@9457: contextPdf.addJRMetadata(source, "station_header", GeneralResultType.station); gernotbelger@9457: contextPdf.addJRMetadata(source, "fix_w", msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER, new Object[] { ri.getWstUnit() })); gernotbelger@9457: contextPdf.addJRMetadata(source, "w_at_gauge_header", msg("fix.export.csv.w_at_gauge")); gernotbelger@9457: contextPdf.addJRMetadata(source, "fix_q", msg(CSV_Q_HEADER)); gernotbelger@9457: contextPdf.addJRMetadata(source, "waterlevel_name_header", msg("common.export.csv.header.mainvalue_label")); gernotbelger@9457: contextPdf.addJRMetadata(source, "location_header", msg("common.export.csv.header.location")); gernotbelger@9457: contextPdf.addJRMetadata(source, "gauge_header", msg("common.export.csv.header.gauge")); gernotbelger@9457: gernotbelger@9457: } gernotbelger@9457: gernotbelger@9457: private List getRows() { gernotbelger@9457: final List list = new ArrayList<>(); gernotbelger@9457: final WQ_MODE mode = RiverUtils.getWQMode((D4EArtifact) this.master); gernotbelger@9457: final boolean atGauge = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.WGAUGE; gernotbelger@9457: final boolean isQ = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.QFREE; gernotbelger@9457: gernotbelger@9457: Double first = Double.NaN; gernotbelger@9457: Double last = Double.NaN; gernotbelger@9457: gernotbelger@9457: for (final WQKms[] tmp : this.data) { gernotbelger@9457: for (final WQKms wqkms : tmp) { gernotbelger@9457: list.addAll(getRows2(wqkms, atGauge, isQ)); gernotbelger@9457: final double[] firstLast = wqkms.getFirstLastKM(); gernotbelger@9457: if (first.isNaN()) { gernotbelger@9457: /* Initialize */ gernotbelger@9457: first = firstLast[0]; gernotbelger@9457: last = firstLast[1]; gernotbelger@9457: } gernotbelger@9457: if (firstLast[0] > firstLast[1]) { gernotbelger@9457: /* gernotbelger@9457: * Calculating upstream we assert that it is gernotbelger@9457: * impossible that the direction changes during this gernotbelger@9457: * loop gernotbelger@9457: */ gernotbelger@9457: first = Math.max(first, firstLast[0]); gernotbelger@9457: last = Math.min(last, firstLast[1]); gernotbelger@9457: } else if (firstLast[0] < firstLast[1]) { gernotbelger@9457: first = Math.min(first, firstLast[0]); gernotbelger@9457: last = Math.max(last, firstLast[1]); gernotbelger@9457: } else { gernotbelger@9457: first = last = firstLast[0]; gernotbelger@9457: } gernotbelger@9457: } gernotbelger@9457: } gernotbelger@9457: gernotbelger@9457: /* Append the official fixing at the bottom */ gernotbelger@9457: for (final WQKms wqkms : this.officalFixings) { gernotbelger@9457: list.addAll(getRows2(filterWQKms(wqkms, first, last), atGauge, isQ)); gernotbelger@9457: } gernotbelger@9457: return list; gernotbelger@9457: } gernotbelger@9457: gernotbelger@9457: protected List getRows2(final WQKms wqkms, final boolean atGauge, final boolean isQ) { gernotbelger@9457: log.debug("WaterlevelExporter.addWKmsData"); // OLD CODE :-/ gernotbelger@9457: gernotbelger@9457: final List list = new ArrayList<>(); gernotbelger@9457: // Skip constant data. gernotbelger@9457: if (wqkms instanceof ConstantWQKms) { gernotbelger@9457: return null; gernotbelger@9457: } gernotbelger@9457: gernotbelger@9457: final NumberFormat kmf = getKmFormatter(); gernotbelger@9457: final NumberFormat wf = getWFormatter(); gernotbelger@9457: final NumberFormat qf = getQFormatter(); gernotbelger@9457: gernotbelger@9457: final int size = wqkms.size(); gernotbelger@9457: double[] result = new double[3]; gernotbelger@9457: gernotbelger@9457: final D4EArtifact flys = (D4EArtifact) this.master; gernotbelger@9457: final RangeAccess rangeAccess = new RangeAccess(flys); gernotbelger@9457: gernotbelger@9457: final Gauge gauge = rangeAccess.getRiver().determineRefGauge(rangeAccess.getKmRange(), rangeAccess.isRange()); gernotbelger@9457: gernotbelger@9457: final String gaugeName = gauge.getName(); gernotbelger@9457: String desc = ""; gernotbelger@9457: final String notinrange = msg(CSV_NOT_IN_GAUGE_RANGE, DEFAULT_CSV_NOT_IN_GAUGE_RANGE); gernotbelger@9457: gernotbelger@9457: final double a = gauge.getRange().getA().doubleValue(); gernotbelger@9457: final double b = gauge.getRange().getB().doubleValue(); gernotbelger@9457: gernotbelger@9457: desc = getDesc(wqkms, isQ); gernotbelger@9457: final long startTime = System.currentTimeMillis(); gernotbelger@9457: gernotbelger@9457: for (int i = 0; i < size; i++) { gernotbelger@9457: result = wqkms.get(i, result); gernotbelger@9457: final double q = result[1]; gernotbelger@9457: final String waterlevel = this.getWaterlevel(q, gauge); // THIS IS NEW (and makes common super method gernotbelger@9457: // difficult) gernotbelger@9457: if (atGauge) { gernotbelger@9457: list.add(new String[] { kmf.format(result[2]), wf.format(result[0]), waterlevel, qf.format(RiverUtils.roundQ(result[1])), desc, gernotbelger@9457: RiverUtils.getLocationDescription(flys, result[2]), result[2] >= a && result[2] <= b ? gaugeName : notinrange }); gernotbelger@9457: } else { gernotbelger@9457: list.add(new String[] { kmf.format(result[2]), wf.format(result[0]), waterlevel, qf.format(RiverUtils.roundQ(result[1])), desc, gernotbelger@9457: RiverUtils.getLocationDescription(flys, result[2]), result[2] >= a && result[2] <= b ? gaugeName : notinrange }); gernotbelger@9457: } gernotbelger@9457: gernotbelger@9457: } gernotbelger@9457: gernotbelger@9457: return list; gernotbelger@9457: } gernotbelger@9457: }