Mercurial > dive4elements > river
view flys-artifacts/src/main/java/de/intevation/flys/exports/WaterlevelExporter.java @ 3643:171db4d5d3cb
FixA: AT export: prevent some numerical problems with steep functions around zero.
flys-artifacts/trunk@5363 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Wed, 05 Sep 2012 08:38:12 +0000 |
parents | 13855a555d3b |
children | 7e7587c905ab |
line wrap: on
line source
package de.intevation.flys.exports; import java.io.IOException; import java.io.OutputStream; import java.text.DateFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Map; import java.util.HashMap; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.w3c.dom.Document; import org.apache.log4j.Logger; import au.com.bytecode.opencsv.CSVWriter; import net.sf.jasperreports.engine.JasperExportManager; import net.sf.jasperreports.engine.JasperFillManager; import net.sf.jasperreports.engine.JasperPrint; import net.sf.jasperreports.engine.JRException; import de.intevation.artifacts.CallContext; import de.intevation.artifacts.CallMeta; import de.intevation.artifacts.common.utils.Config; import de.intevation.flys.model.Gauge; import de.intevation.flys.artifacts.FLYSArtifact; import de.intevation.flys.artifacts.WINFOArtifact; import de.intevation.flys.artifacts.model.CalculationResult; import de.intevation.flys.artifacts.model.WQCKms; import de.intevation.flys.artifacts.model.WQKms; import de.intevation.flys.artifacts.model.WKmsJRDataSource; import de.intevation.flys.artifacts.model.fixings.FixRealizingResult; import de.intevation.flys.artifacts.resources.Resources; import de.intevation.flys.utils.FLYSUtils; import de.intevation.flys.utils.FLYSUtils.WQ_MODE; import de.intevation.flys.utils.Formatter; /** * Generates different output formats (wst, csv, pdf) of data that resulted from * a waterlevel computation. * * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> */ public class WaterlevelExporter extends AbstractExporter { /** The logger used in this exporter.*/ private static Logger logger = Logger.getLogger(WaterlevelExporter.class); public static final String FACET_WST = "wst"; public static final String CSV_KM_HEADER = "export.waterlevel.csv.header.km"; public static final String CSV_W_HEADER = "export.waterlevel.csv.header.w"; public static final String CSV_Q_HEADER = "export.waterlevel.csv.header.q"; public static final String CSV_Q_DESC_HEADER = "export.waterlevel.csv.header.q.desc"; public static final String CSV_W_DESC_HEADER = "export.waterlevel.csv.header.w.desc"; public static final String CSV_LOCATION_HEADER = "export.waterlevel.csv.header.location"; public static final String CSV_GAUGE_HEADER = "export.waterlevel.csv.header.gauge"; public static final String CSV_META_RESULT = "export.waterlevel.csv.meta.result"; public static final String CSV_META_CREATION = "export.waterlevel.csv.meta.creation"; public static final String CSV_META_CALCULATIONBASE = "export.waterlevel.csv.meta.calculationbase"; public static final String CSV_META_RIVER = "export.waterlevel.csv.meta.river"; public static final String CSV_META_RANGE = "export.waterlevel.csv.meta.range"; public static final String CSV_META_GAUGE = "export.waterlevel.csv.meta.gauge"; public static final String CSV_META_Q = "export.waterlevel.csv.meta.q"; public static final String CSV_META_W = "export.waterlevel.csv.meta.w"; public static final String CSV_NOT_IN_GAUGE_RANGE = "export.waterlevel.csv.not.in.gauge.range"; public static final Pattern NUMBERS_PATTERN = Pattern.compile("\\D*(\\d++.\\d*)\\D*"); public static final String DEFAULT_CSV_KM_HEADER = "Fluss-Km"; public static final String DEFAULT_CSV_W_HEADER = "W [NN + m]"; public static final String DEFAULT_CSV_Q_HEADER = "Q [m\u00b3/s]"; public static final String DEFAULT_CSV_Q_DESC_HEADER = "Bezeichnung"; public static final String DEFAULT_CSV_W_DESC_HEADER = "W/Pegel [cm]"; public static final String DEFAULT_CSV_LOCATION_HEADER = "Lage"; public static final String DEFAULT_CSV_GAUGE_HEADER = "Bezugspegel"; public static final String DEFAULT_CSV_NOT_IN_GAUGE_RANGE = "außerhalb des gewählten Bezugspegels"; public static final String PDF_HEADER_MODE = "export.waterlevel.pdf.mode"; public static final String JASPER_FILE = "export.waterlevel.pdf.file"; /** The storage that contains all WQKms objects for the different facets.*/ protected List<WQKms[]> data; public void init(Document request, OutputStream out, CallContext context) { logger.debug("WaterlevelExporter.init"); super.init(request, out, context); this.data = new ArrayList<WQKms[]>(); } @Override public void generate() throws IOException { logger.debug("WaterlevelExporter.generate"); if (facet != null && facet.equals(AbstractExporter.FACET_CSV)) { generateCSV(); } else if (facet != null && facet.equals(FACET_WST)) { generateWST(); } else if (facet != null && facet.equals(AbstractExporter.FACET_PDF)) { generatePDF(); } else { throw new IOException("invalid facet for exporter"); } } @Override protected void addData(Object d) { if (d instanceof CalculationResult) { d = ((CalculationResult)d).getData(); if (d instanceof WQKms []) { data.add((WQKms [])d); } else if (d instanceof FixRealizingResult) { data.add(((FixRealizingResult) d).getWQKms()); } } } /** * This method is used to prepare the column titles of waterlevel exports. * Titles in this export include the Q value. If a Q value matches a named * main value (as HQ100 or MNQ) this named main value should be used as * title. This method resets the name of the <i>wqkms</i> object if such * named main value fits to the chosen Q. * * @param winfo A WINFO Artifact. * @param wqkms A WQKms object that should be prepared. */ protected String getColumnTitle(WINFOArtifact winfo, WQKms wqkms) { logger.debug("WaterlevelExporter.prepareNamedValue"); String name = wqkms.getName(); logger.debug("Name of WQKms = '" + name + "'"); if (name.indexOf("W=") >= 0) { return name; } Matcher m = NUMBERS_PATTERN.matcher(name); if (m.matches()) { String raw = m.group(1); try { double v = Double.valueOf(raw); String nmv = FLYSUtils.getNamedMainValue(winfo, v); if (nmv != null && nmv.length() > 0) { nmv = FLYSUtils.stripNamedMainValue(nmv); nmv += "=" + String.valueOf(v); logger.debug("Set named main value '" + nmv + "'"); return nmv; } } catch (NumberFormatException nfe) { // do nothing here } } return name; } protected String getCSVRowTitle(WINFOArtifact winfo, WQKms wqkms) { logger.debug("WaterlevelExporter.prepareNamedValue"); String name = wqkms.getName(); logger.debug("Name of WQKms = '" + name + "'"); WQ_MODE wqmode = FLYSUtils.getWQMode(winfo); if (wqmode == WQ_MODE.WFREE || wqmode == WQ_MODE.QGAUGE) { return localizeWQKms(winfo, wqkms); } Double v = wqkms.getRawValue(); String nmv = FLYSUtils.getNamedMainValue(winfo, v); if (nmv != null && nmv.length() > 0) { nmv = FLYSUtils.stripNamedMainValue(nmv); logger.debug("Set named main value '" + nmv + "'"); return nmv; } return localizeWQKms(winfo, wqkms); } /** * Get a string like 'W=' or 'Q=' with a number following in localized * format. */ protected String localizeWQKms(WINFOArtifact winfo, WQKms wqkms) { WQ_MODE wqmode = FLYSUtils.getWQMode(winfo); Double rawValue = wqkms.getRawValue(); if (rawValue == null) { return wqkms.getName(); } NumberFormat nf = Formatter.getRawFormatter(context); if (wqmode == WQ_MODE.WFREE || wqmode == WQ_MODE.WGAUGE) { return "W=" + nf.format(rawValue); } else { return "Q=" + nf.format(rawValue); } } @Override protected void writeCSVData(CSVWriter writer) { logger.info("WaterlevelExporter.writeData"); WQ_MODE mode = FLYSUtils.getWQMode((FLYSArtifact)master); boolean atGauge = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.WGAUGE; boolean isQ = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.QFREE; FLYSUtils.WQ_INPUT input = FLYSUtils.getWQInputMode((FLYSArtifact)master); writeCSVMeta(writer); writeCSVHeader(writer, atGauge, isQ); for (WQKms[] tmp: data) { for (WQKms wqkms: tmp) { wQKms2CSV(writer, wqkms, atGauge, isQ); } } } protected void writeCSVMeta(CSVWriter writer) { logger.info("WaterlevelExporter.writeCSVMeta"); CallMeta meta = context.getMeta(); FLYSArtifact flys = (FLYSArtifact) master; writer.writeNext(new String[] { Resources.getMsg( meta, CSV_META_RESULT, CSV_META_RESULT, new Object[] { FLYSUtils.getRivername(flys) }) }); Locale locale = Resources.getLocale(meta); DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale); writer.writeNext(new String[] { Resources.getMsg( meta, CSV_META_CREATION, CSV_META_CREATION, new Object[] { df.format(new Date()) }) }); writer.writeNext(new String[] { Resources.getMsg( meta, CSV_META_CALCULATIONBASE, CSV_META_CALCULATIONBASE, new Object[] { "" }) // TODO what is required at this place? }); writer.writeNext(new String[] { Resources.getMsg( meta, CSV_META_RIVER, CSV_META_RIVER, new Object[] { FLYSUtils.getRivername(flys) }) }); double[] kms = FLYSUtils.getKmRange(flys); writer.writeNext(new String[] { Resources.getMsg( meta, CSV_META_RANGE, CSV_META_RANGE, new Object[] { kms[0], kms[kms.length-1] }) }); writer.writeNext(new String[] { Resources.getMsg( meta, CSV_META_GAUGE, CSV_META_GAUGE, new Object[] { FLYSUtils.getGaugename(flys) }) }); FLYSUtils.WQ_MODE wq = FLYSUtils.getWQMode(flys); if (wq == FLYSUtils.WQ_MODE.QFREE || wq == FLYSUtils.WQ_MODE.QGAUGE) { double[] qs = FLYSUtils.getQs(flys); FLYSUtils.WQ_INPUT input = FLYSUtils.getWQInputMode(flys); String data = ""; if ((input == FLYSUtils.WQ_INPUT.ADAPTED || input == FLYSUtils.WQ_INPUT.RANGE) && qs != null && qs.length > 0) { data = String.valueOf(qs[0]); data += " - " + String.valueOf(qs[qs.length-1]); } else if (input == FLYSUtils.WQ_INPUT.SINGLE && qs != null){ data = String.valueOf(qs[0]); for (int i = 1; i < qs.length; i++) { data += ", " + String.valueOf(qs[i]); } } else { logger.warn("Could not determine Q range!"); } writer.writeNext(new String[] { Resources.getMsg( meta, CSV_META_Q, CSV_META_Q, new Object[] {data}) }); } else { double[] ws = FLYSUtils.getWs(flys); String lower = ""; String upper = ""; if (ws != null && ws.length > 0) { lower = String.valueOf(ws[0]); upper = String.valueOf(ws[ws.length-1]); } else { logger.warn("Could not determine W range!"); } writer.writeNext(new String[] { Resources.getMsg( meta, CSV_META_W, CSV_META_W, new Object[] { lower, upper }) }); } writer.writeNext(new String[] { "" }); } protected void writeCSVHeader( CSVWriter writer, boolean atGauge, boolean isQ ) { logger.info("WaterlevelExporter.writeCSVHeader"); if (atGauge) { writer.writeNext(new String[] { msg(CSV_KM_HEADER, DEFAULT_CSV_KM_HEADER), msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER), msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER), (isQ ? msg(CSV_Q_DESC_HEADER, DEFAULT_CSV_Q_DESC_HEADER) : msg(CSV_W_DESC_HEADER, DEFAULT_CSV_W_DESC_HEADER)), msg(CSV_LOCATION_HEADER, DEFAULT_CSV_LOCATION_HEADER), msg(CSV_GAUGE_HEADER, DEFAULT_CSV_GAUGE_HEADER) }); } else { writer.writeNext(new String[] { msg(CSV_KM_HEADER, DEFAULT_CSV_KM_HEADER), msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER), msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER), msg(CSV_LOCATION_HEADER, DEFAULT_CSV_LOCATION_HEADER) }); } } /** * Write "rows" of csv data from wqkms with writer. */ protected void wQKms2CSV( CSVWriter writer, WQKms wqkms, boolean atGauge, boolean isQ ) { logger.debug("WaterlevelExporter.wQKms2CSV"); NumberFormat kmf = getKmFormatter(); NumberFormat wf = getWFormatter(); NumberFormat qf = getQFormatter(); int size = wqkms.size(); double[] result = new double[3]; FLYSArtifact flys = (FLYSArtifact) master; Gauge gauge = FLYSUtils.getGauge(flys); String gaugeName = gauge.getName(); String desc = ""; String notinrange = msg( CSV_NOT_IN_GAUGE_RANGE, DEFAULT_CSV_NOT_IN_GAUGE_RANGE); double a = gauge.getRange().getA().doubleValue(); double b = gauge.getRange().getB().doubleValue(); if (flys instanceof WINFOArtifact && isQ) { desc = getCSVRowTitle((WINFOArtifact)flys, wqkms); } else if (!isQ) { Double value = FLYSUtils.getValueFromWQ(wqkms); desc = value != null ? Formatter.getWaterlevelW(context).format(value) : null; } long startTime = System.currentTimeMillis(); String colDesc = desc; if (flys instanceof WINFOArtifact) { if (wqkms != null && wqkms.getRawValue() != null) { WINFOArtifact winfo = (WINFOArtifact) flys; colDesc = FLYSUtils.getNamedMainValue(winfo, wqkms.getRawValue()); } } for (int i = 0; i < size; i ++) { result = wqkms.get(i, result); if (atGauge) { writer.writeNext(new String[] { kmf.format(result[2]), wf.format(result[0]), qf.format(result[1]), colDesc, FLYSUtils.getLocationDescription(flys, result[2]), result[2] >= a && result[2] <= b ? gaugeName : notinrange }); } else { writer.writeNext(new String[] { kmf.format(result[2]), wf.format(result[0]), qf.format(result[1]), FLYSUtils.getLocationDescription(flys, result[2]) }); } } long stopTime = System.currentTimeMillis(); if (logger.isDebugEnabled()) { logger.debug("Writing CSV took " + (float)(stopTime-startTime)/1000f + " secs."); } } /** * Generates the output in WST format. */ protected void generateWST() throws IOException { logger.info("WaterlevelExporter.generateWST"); int cols = data.get(0).length; WstWriter writer = new WstWriter(cols); writeWSTData(writer); writer.write(out); } protected void writeWSTData(WstWriter writer) { logger.debug("WaterlevelExporter.writeWSTData"); double[] result = new double[4]; for (WQKms[] tmp: data) { for (WQKms wqkms: tmp) { int size = wqkms != null ? wqkms.size() : 0; addWSTColumn(writer, wqkms); for (int i = 0; i < size; i++) { result = wqkms.get(i, result); writer.add(result); } if (wqkms instanceof WQCKms) { addWSTColumn(writer, wqkms); for (int c = 0; c < size; c++) { result = wqkms.get(c, result); writer.addCorrected(result); } } } } } /** * This method is used to register a new column at <i>writer</i>. The name / * title of the column depends on the Q or W value of <i>wqkms</i>. If a Q * was selected and the Q fits to a named main value, the title is set to * the named main value. Otherwise, the name returned by * <i>WQKms.getName()</i> is set. * * @param writer The WstWriter. * @param wqkms The new WST column. */ protected void addWSTColumn(WstWriter writer, WQKms wqkms) { if (master instanceof WINFOArtifact) { writer.addColumn(getColumnTitle((WINFOArtifact) master, wqkms)); } else { writer.addColumn(wqkms.getName()); } } /** * */ @Override protected void writePDF(OutputStream out) { logger.debug("write PDF"); WKmsJRDataSource source = createJRData(); String jasperFile = Resources.getMsg( context.getMeta(), JASPER_FILE, "/jasper/waterlevel_en.jasper"); String confPath = Config.getConfigDirectory().toString(); Map parameters = new HashMap(); parameters.put("ReportTitle", "Exported Data"); try { JasperPrint print = JasperFillManager.fillReport( confPath + jasperFile, parameters, source); JasperExportManager.exportReportToPdfStream(print, out); } catch(JRException je) { logger.warn("Error generating PDF Report!"); je.printStackTrace(); } } protected WKmsJRDataSource createJRData() { WKmsJRDataSource source = new WKmsJRDataSource(); WQ_MODE mode = FLYSUtils.getWQMode((FLYSArtifact)master); boolean atGauge = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.WGAUGE; boolean isQ = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.QFREE; addMetaData(source); for (WQKms[] tmp: data) { for (WQKms wqkms: tmp) { addWKmsData(wqkms, atGauge, isQ, source); } } return source; } protected void addMetaData(WKmsJRDataSource source) { CallMeta meta = context.getMeta(); FLYSArtifact flys = (FLYSArtifact) master; source.addMetaData ("river", FLYSUtils.getRivername(flys)); Locale locale = Resources.getLocale(meta); DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale); source.addMetaData("date", df.format(new Date())); double[] kms = FLYSUtils.getKmRange(flys); source.addMetaData("range", kms[0] + " - " + kms[kms.length-1]); source.addMetaData("gauge", FLYSUtils.getGaugename(flys)); source.addMetaData("calculation", Resources.getMsg( locale, PDF_HEADER_MODE, "Waterlevel")); } protected void addWKmsData( WQKms wqkms, boolean atGauge, boolean isQ, WKmsJRDataSource source) { logger.debug("WaterlevelExporter.addWKmsData"); NumberFormat kmf = getKmFormatter(); NumberFormat wf = getWFormatter(); NumberFormat qf = getQFormatter(); int size = wqkms.size(); double[] result = new double[3]; FLYSArtifact flys = (FLYSArtifact) master; Gauge gauge = FLYSUtils.getGauge(flys); String gaugeName = gauge.getName(); String desc = ""; String notinrange = msg( CSV_NOT_IN_GAUGE_RANGE, DEFAULT_CSV_NOT_IN_GAUGE_RANGE); double a = gauge.getRange().getA().doubleValue(); double b = gauge.getRange().getB().doubleValue(); if (flys instanceof WINFOArtifact && isQ) { desc = getCSVRowTitle((WINFOArtifact)flys, wqkms); } else if (!isQ) { Double value = FLYSUtils.getValueFromWQ(wqkms); desc = value != null ? Formatter.getWaterlevelW(context).format(value) : null; } long startTime = System.currentTimeMillis(); for (int i = 0; i < size; i ++) { result = wqkms.get(i, result); if (atGauge) { source.addData(new String[] { kmf.format(result[2]), wf.format(result[0]), qf.format(result[1]), desc, FLYSUtils.getLocationDescription(flys, result[2]), result[2] >= a && result[2] <= b ? gaugeName : notinrange }); } else { source.addData(new String[] { kmf.format(result[2]), wf.format(result[0]), qf.format(result[1]), desc, FLYSUtils.getLocationDescription(flys, result[2]), result[2] >= a && result[2] <= b ? gaugeName : notinrange }); } } long stopTime = System.currentTimeMillis(); if (logger.isDebugEnabled()) { logger.debug("Writing PDF data took " + (float)(stopTime-startTime)/1000f + " secs."); } } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :